cap-creatives-ui 8.0.280 → 8.0.321
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/.github/workflows/pr-title-check.yml +88 -0
- package/app/constants/unified.js +21 -1
- package/app/containers/App/constants.js +0 -1
- package/app/containers/Login/test/index.test.js +123 -0
- package/app/containers/Login/test/selectors.test.js +165 -0
- package/app/initialState.js +0 -2
- package/app/services/api.js +6 -0
- package/app/services/tests/api.test.js +7 -0
- package/app/services/tests/getSchema.test.js +95 -0
- package/app/utils/common.js +23 -9
- package/app/utils/commonUtils.js +64 -93
- package/app/utils/tagValidations.js +83 -219
- package/app/utils/templateVarUtils.js +172 -0
- package/app/utils/tests/common.test.js +265 -323
- package/app/utils/tests/commonUtil.test.js +461 -118
- package/app/utils/tests/commonUtils.test.js +581 -0
- package/app/utils/tests/messageUtils.test.js +95 -0
- package/app/utils/tests/smsCharCount.test.js +304 -0
- package/app/utils/tests/smsCharCountV2.test.js +213 -10
- package/app/utils/tests/tagValidations.test.js +474 -357
- package/app/utils/tests/templateVarUtils.test.js +160 -0
- package/app/v2Components/CapDeviceContent/index.js +10 -7
- package/app/v2Components/CapTagList/index.js +32 -24
- package/app/v2Components/CapTagList/style.scss +48 -0
- package/app/v2Components/CapTagListWithInput/__tests__/CapTagListWithInput.test.js +63 -0
- package/app/v2Components/CapTagListWithInput/index.js +8 -0
- package/app/v2Components/CapWhatsappCTA/index.js +2 -0
- package/app/v2Components/CapWhatsappCarouselButton/index.js +32 -14
- package/app/v2Components/CapWhatsappCarouselButton/tests/index.test.js +120 -2
- package/app/v2Components/CommonTestAndPreview/CustomValuesEditor.js +70 -49
- package/app/v2Components/CommonTestAndPreview/DeliverySettings/DeliverySettings.scss +39 -0
- package/app/v2Components/CommonTestAndPreview/DeliverySettings/ModifyDeliverySettings.js +606 -0
- package/app/v2Components/CommonTestAndPreview/DeliverySettings/ModifyDeliverySettings.scss +36 -0
- package/app/v2Components/CommonTestAndPreview/DeliverySettings/constants.js +79 -0
- package/app/v2Components/CommonTestAndPreview/DeliverySettings/index.js +314 -0
- package/app/v2Components/CommonTestAndPreview/DeliverySettings/messages.js +141 -0
- package/app/v2Components/CommonTestAndPreview/DeliverySettings/utils/parseSenderDetailsResponse.js +156 -0
- package/app/v2Components/CommonTestAndPreview/SendTestMessage.js +57 -1
- package/app/v2Components/CommonTestAndPreview/UnifiedPreview/_unifiedPreview.scss +20 -1
- package/app/v2Components/CommonTestAndPreview/UnifiedPreview/index.js +133 -4
- package/app/v2Components/CommonTestAndPreview/_commonTestAndPreview.scss +210 -4
- package/app/v2Components/CommonTestAndPreview/actions.js +20 -0
- package/app/v2Components/CommonTestAndPreview/constants.js +57 -1
- package/app/v2Components/CommonTestAndPreview/index.js +878 -156
- package/app/v2Components/CommonTestAndPreview/messages.js +41 -3
- package/app/v2Components/CommonTestAndPreview/previewApiUtils.js +59 -0
- package/app/v2Components/CommonTestAndPreview/reducer.js +47 -0
- package/app/v2Components/CommonTestAndPreview/sagas.js +75 -5
- package/app/v2Components/CommonTestAndPreview/selectors.js +51 -0
- package/app/v2Components/CommonTestAndPreview/tests/CustomValuesEditor.test.js +352 -0
- package/app/v2Components/CommonTestAndPreview/tests/DeliverySettings/ModifyDeliverySettings.test.js +1156 -0
- package/app/v2Components/CommonTestAndPreview/tests/DeliverySettings/index.test.js +334 -0
- package/app/v2Components/CommonTestAndPreview/tests/DeliverySettings/utils/parseSenderDetailsResponse.test.js +576 -0
- package/app/v2Components/CommonTestAndPreview/tests/SendTestMessage.test.js +156 -0
- package/app/v2Components/CommonTestAndPreview/tests/UnifiedPreview/index.test.js +199 -1
- package/app/v2Components/CommonTestAndPreview/tests/actions.test.js +50 -0
- package/app/v2Components/CommonTestAndPreview/tests/constants.test.js +18 -7
- package/app/v2Components/CommonTestAndPreview/tests/index.test.js +914 -5
- package/app/v2Components/CommonTestAndPreview/tests/previewApiUtils.test.js +67 -0
- package/app/v2Components/CommonTestAndPreview/tests/reducer.test.js +118 -0
- package/app/v2Components/CommonTestAndPreview/tests/sagas.test.js +146 -378
- package/app/v2Components/CommonTestAndPreview/tests/selectors.test.js +146 -0
- package/app/v2Components/ErrorInfoNote/index.js +24 -26
- package/app/v2Components/FormBuilder/index.js +182 -204
- package/app/v2Components/FormBuilder/messages.js +4 -8
- package/app/v2Components/HtmlEditor/HTMLEditor.js +7 -6
- package/app/v2Components/HtmlEditor/__tests__/HTMLEditor.apiErrors.test.js +1 -1
- package/app/v2Components/HtmlEditor/__tests__/HTMLEditor.test.js +928 -17
- package/app/v2Components/HtmlEditor/components/CodeEditorPane/index.js +4 -2
- package/app/v2Components/HtmlEditor/hooks/__tests__/useValidation.test.js +452 -3
- package/app/v2Components/HtmlEditor/hooks/useValidation.js +12 -9
- package/app/v2Components/HtmlEditor/utils/__tests__/htmlValidator.enhanced.test.js +132 -0
- package/app/v2Components/HtmlEditor/utils/htmlValidator.js +4 -2
- package/app/v2Components/SmsFallback/SmsFallbackLocalSelector.js +87 -0
- package/app/v2Components/SmsFallback/constants.js +73 -0
- package/app/v2Components/SmsFallback/index.js +956 -0
- package/app/v2Components/SmsFallback/index.scss +265 -0
- package/app/v2Components/SmsFallback/messages.js +78 -0
- package/app/v2Components/SmsFallback/smsFallbackUtils.js +107 -0
- package/app/v2Components/SmsFallback/tests/SmsFallbackLocalSelector.test.js +50 -0
- package/app/v2Components/SmsFallback/tests/rcsSmsFallback.acceptance.test.js +147 -0
- package/app/v2Components/SmsFallback/tests/smsFallbackHandlers.test.js +304 -0
- package/app/v2Components/SmsFallback/tests/smsFallbackUi.test.js +197 -0
- package/app/v2Components/SmsFallback/tests/smsFallbackUtils.test.js +261 -0
- package/app/v2Components/SmsFallback/tests/useLocalTemplateList.test.js +422 -0
- package/app/v2Components/SmsFallback/useLocalTemplateList.js +92 -0
- package/app/v2Components/TestAndPreviewSlidebox/index.js +22 -1
- package/app/v2Components/TestAndPreviewSlidebox/sagas.js +11 -4
- package/app/v2Components/TestAndPreviewSlidebox/tests/saga.test.js +3 -1
- package/app/v2Components/VarSegmentMessageEditor/constants.js +2 -0
- package/app/v2Components/VarSegmentMessageEditor/index.js +125 -0
- package/app/v2Components/VarSegmentMessageEditor/index.scss +46 -0
- package/app/v2Containers/BeeEditor/index.js +3 -0
- package/app/v2Containers/BeePopupEditor/index.js +9 -2
- package/app/v2Containers/Cap/mockData.js +0 -14
- package/app/v2Containers/Cap/reducer.js +3 -55
- package/app/v2Containers/Cap/tests/reducer.test.js +0 -102
- package/app/v2Containers/CommunicationFlow/CommunicationFlow.js +291 -0
- package/app/v2Containers/CommunicationFlow/CommunicationFlow.scss +25 -0
- package/app/v2Containers/CommunicationFlow/Tests/CommunicationFlow.test.js +255 -0
- package/app/v2Containers/CommunicationFlow/constants.js +200 -0
- package/app/v2Containers/CommunicationFlow/index.js +102 -0
- package/app/v2Containers/CommunicationFlow/messages.js +346 -0
- package/app/v2Containers/CommunicationFlow/steps/ChannelSelectionStep/ChannelSelectionStep.js +522 -0
- package/app/v2Containers/CommunicationFlow/steps/ChannelSelectionStep/ChannelSelectionStep.scss +170 -0
- package/app/v2Containers/CommunicationFlow/steps/ChannelSelectionStep/Tests/ChannelSelectionStep.test.js +796 -0
- package/app/v2Containers/CommunicationFlow/steps/ChannelSelectionStep/index.js +5 -0
- package/app/v2Containers/CommunicationFlow/steps/CommunicationStrategyStep/CommunicationStrategyStep.js +95 -0
- package/app/v2Containers/CommunicationFlow/steps/CommunicationStrategyStep/Tests/CommunicationStrategyStep.test.js +133 -0
- package/app/v2Containers/CommunicationFlow/steps/CommunicationStrategyStep/index.js +5 -0
- package/app/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/DeliverySettingsSection.js +289 -0
- package/app/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/DeliverySettingsSection.scss +70 -0
- package/app/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/SenderDetails.js +319 -0
- package/app/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/SenderDetails.scss +69 -0
- package/app/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/DeliverySettingsSection.test.js +616 -0
- package/app/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/SenderDetails.test.js +577 -0
- package/app/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/deliverySettingsConfig.test.js +1111 -0
- package/app/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/deliverySettingsConfig.js +696 -0
- package/app/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/index.js +7 -0
- package/app/v2Containers/CommunicationFlow/steps/DynamicControlsStep/DynamicControlsStep.js +102 -0
- package/app/v2Containers/CommunicationFlow/steps/DynamicControlsStep/DynamicControlsStep.scss +36 -0
- package/app/v2Containers/CommunicationFlow/steps/DynamicControlsStep/Tests/DynamicControlsStep.test.js +91 -0
- package/app/v2Containers/CommunicationFlow/steps/DynamicControlsStep/index.js +5 -0
- package/app/v2Containers/CommunicationFlow/steps/MessageTypeStep/MessageTypeStep.js +86 -0
- package/app/v2Containers/CommunicationFlow/steps/MessageTypeStep/Tests/MessageTypeStep.test.js +100 -0
- package/app/v2Containers/CommunicationFlow/steps/MessageTypeStep/index.js +5 -0
- package/app/v2Containers/CommunicationFlow/utils/getEnabledSteps.js +30 -0
- package/app/v2Containers/CreativesContainer/CreativesSlideBoxWrapper.js +43 -0
- package/app/v2Containers/CreativesContainer/SlideBoxContent.js +127 -11
- package/app/v2Containers/CreativesContainer/SlideBoxFooter.js +62 -9
- package/app/v2Containers/CreativesContainer/SlideBoxHeader.js +29 -4
- package/app/v2Containers/CreativesContainer/constants.js +24 -0
- package/app/v2Containers/CreativesContainer/embeddedSlideboxUtils.js +67 -0
- package/app/v2Containers/CreativesContainer/index.js +346 -71
- package/app/v2Containers/CreativesContainer/index.scss +51 -1
- package/app/v2Containers/CreativesContainer/messages.js +12 -0
- package/app/v2Containers/CreativesContainer/tests/SlideBoxContent.localTemplates.test.js +90 -0
- package/app/v2Containers/CreativesContainer/tests/SlideBoxContent.test.js +69 -1
- package/app/v2Containers/CreativesContainer/tests/SlideBoxFooter.test.js +443 -0
- package/app/v2Containers/CreativesContainer/tests/SlideBoxHeader.test.js +110 -0
- package/app/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +147 -4
- package/app/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxHeader.test.js.snap +363 -0
- package/app/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +57 -10
- package/app/v2Containers/CreativesContainer/tests/embeddedSlideboxUtils.test.js +258 -0
- package/app/v2Containers/CreativesContainer/tests/index.test.js +71 -9
- package/app/v2Containers/CreativesContainer/tests/useLocalTemplatesProp.test.js +125 -0
- package/app/v2Containers/Email/index.js +2 -5
- package/app/v2Containers/EmailWrapper/components/EmailHTMLEditor.js +58 -77
- package/app/v2Containers/EmailWrapper/components/EmailWrapperView.js +3 -0
- package/app/v2Containers/EmailWrapper/components/__tests__/EmailHTMLEditor.test.js +158 -89
- package/app/v2Containers/EmailWrapper/components/__tests__/EmailWrapperView.test.js +16 -1
- package/app/v2Containers/EmailWrapper/hooks/useEmailWrapper.js +17 -12
- package/app/v2Containers/EmailWrapper/index.js +4 -0
- package/app/v2Containers/EmailWrapper/tests/useEmailWrapper.edgeCases.test.js +1 -0
- package/app/v2Containers/EmailWrapper/tests/useEmailWrapper.test.js +133 -0
- package/app/v2Containers/FTP/index.js +2 -51
- package/app/v2Containers/FTP/messages.js +0 -4
- package/app/v2Containers/InApp/__tests__/InAppHTMLEditor.test.js +110 -155
- package/app/v2Containers/InApp/index.js +297 -118
- package/app/v2Containers/InApp/tests/index.test.js +17 -6
- package/app/v2Containers/InApp/tests/mockData.js +1 -1
- package/app/v2Containers/InAppWrapper/hooks/__tests__/useInAppWrapper.test.js +19 -0
- package/app/v2Containers/InAppWrapper/hooks/useInAppWrapper.js +3 -0
- package/app/v2Containers/InAppWrapper/index.js +3 -0
- package/app/v2Containers/InappAdvance/index.js +5 -104
- package/app/v2Containers/InappAdvance/tests/index.test.js +2 -0
- package/app/v2Containers/Line/Container/ImageCarousel/tests/__snapshots__/content.test.js.snap +24 -3
- package/app/v2Containers/Line/Container/Text/index.js +0 -1
- package/app/v2Containers/MobilePush/Create/index.js +105 -28
- package/app/v2Containers/MobilePush/Create/messages.js +4 -0
- package/app/v2Containers/MobilePush/Edit/index.js +250 -68
- package/app/v2Containers/MobilePush/Edit/messages.js +4 -0
- package/app/v2Containers/MobilePushNew/components/PlatformContentFields.js +36 -12
- package/app/v2Containers/MobilePushNew/components/tests/PlatformContentFields.test.js +68 -27
- package/app/v2Containers/MobilePushNew/index.js +78 -35
- package/app/v2Containers/MobilePushNew/messages.js +8 -0
- package/app/v2Containers/MobilepushWrapper/index.js +11 -1
- package/app/v2Containers/Rcs/constants.js +32 -1
- package/app/v2Containers/Rcs/index.js +963 -916
- package/app/v2Containers/Rcs/index.scss +85 -6
- package/app/v2Containers/Rcs/messages.js +10 -1
- package/app/v2Containers/Rcs/rcsLibraryHydrationUtils.js +205 -0
- package/app/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +41136 -1566
- package/app/v2Containers/Rcs/tests/__snapshots__/utils.test.js.snap +0 -5
- package/app/v2Containers/Rcs/tests/index.test.js +41 -38
- package/app/v2Containers/Rcs/tests/mockData.js +38 -0
- package/app/v2Containers/Rcs/tests/rcsLibraryHydrationUtils.test.js +251 -0
- package/app/v2Containers/Rcs/tests/utils.test.js +379 -1
- package/app/v2Containers/Rcs/utils.js +358 -10
- package/app/v2Containers/Sms/Create/index.js +122 -39
- package/app/v2Containers/Sms/Create/messages.js +4 -0
- package/app/v2Containers/Sms/Edit/index.js +37 -3
- package/app/v2Containers/Sms/commonMethods.js +3 -6
- package/app/v2Containers/Sms/smsFormDataHelpers.js +67 -0
- package/app/v2Containers/Sms/tests/commonMethods.test.js +122 -0
- package/app/v2Containers/Sms/tests/smsFormDataHelpers.test.js +253 -0
- package/app/v2Containers/SmsTrai/Create/index.js +9 -4
- package/app/v2Containers/SmsTrai/Create/index.scss +1 -1
- package/app/v2Containers/SmsTrai/Edit/constants.js +2 -0
- package/app/v2Containers/SmsTrai/Edit/index.js +667 -160
- package/app/v2Containers/SmsTrai/Edit/index.scss +121 -0
- package/app/v2Containers/SmsTrai/Edit/messages.js +9 -4
- package/app/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +4590 -2436
- package/app/v2Containers/SmsWrapper/index.js +41 -8
- package/app/v2Containers/TagList/index.js +63 -2
- package/app/v2Containers/TagList/messages.js +8 -0
- package/app/v2Containers/TagList/tests/TagList.test.js +122 -20
- package/app/v2Containers/TagList/tests/mockdata.js +17 -0
- package/app/v2Containers/Templates/TemplatesActionBar.js +101 -0
- package/app/v2Containers/Templates/_templates.scss +61 -2
- package/app/v2Containers/Templates/actions.js +11 -0
- package/app/v2Containers/Templates/constants.js +2 -0
- package/app/v2Containers/Templates/index.js +90 -40
- package/app/v2Containers/Templates/reducer.js +3 -1
- package/app/v2Containers/Templates/sagas.js +57 -12
- package/app/v2Containers/Templates/tests/TemplatesActionBar.test.js +120 -0
- package/app/v2Containers/Templates/tests/__snapshots__/index.test.js.snap +1043 -1079
- package/app/v2Containers/Templates/tests/reducer.test.js +12 -0
- package/app/v2Containers/Templates/tests/sagas.test.js +193 -12
- package/app/v2Containers/Templates/tests/smsTemplatesListApi.test.js +180 -0
- package/app/v2Containers/Templates/utils/smsTemplatesListApi.js +79 -0
- package/app/v2Containers/TemplatesV2/TemplatesV2.style.js +72 -1
- package/app/v2Containers/TemplatesV2/index.js +147 -49
- package/app/v2Containers/TemplatesV2/tests/TemplatesV2.localTemplates.test.js +131 -0
- package/app/v2Containers/Viber/index.js +9 -10
- package/app/v2Containers/Viber/index.scss +1 -1
- package/app/v2Containers/WebPush/Create/components/BrandIconSection.test.js +264 -0
- package/app/v2Containers/WebPush/Create/components/MessageSection.js +78 -19
- package/app/v2Containers/WebPush/Create/components/MessageSection.test.js +82 -0
- package/app/v2Containers/WebPush/Create/components/__snapshots__/BrandIconSection.test.js.snap +187 -0
- package/app/v2Containers/WebPush/Create/components/__snapshots__/MessageSection.test.js.snap +25 -17
- package/app/v2Containers/WebPush/Create/hooks/useAiraTriggerPosition.js +80 -0
- package/app/v2Containers/WebPush/Create/hooks/useAiraTriggerPosition.test.js +210 -0
- package/app/v2Containers/WebPush/Create/hooks/useTagManagement.js +1 -5
- package/app/v2Containers/WebPush/Create/hooks/useTagManagement.test.js +0 -7
- package/app/v2Containers/WebPush/Create/index.js +36 -6
- package/app/v2Containers/WebPush/Create/index.scss +5 -0
- package/app/v2Containers/WebPush/Create/messages.js +8 -1
- package/app/v2Containers/WebPush/Create/preview/tests/NotificationContainer.test.js +269 -0
- package/app/v2Containers/WebPush/Create/utils/validation.js +31 -15
- package/app/v2Containers/WebPush/Create/utils/validation.test.js +72 -24
- package/app/v2Containers/Whatsapp/index.js +28 -53
- package/app/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +26939 -3982
- package/app/v2Containers/Whatsapp/tests/index.test.js +172 -0
- package/app/v2Containers/Zalo/index.js +5 -11
- package/package.json +2 -2
- package/version +9 -0
|
@@ -11,6 +11,8 @@ import {
|
|
|
11
11
|
import '@testing-library/jest-dom';
|
|
12
12
|
import { IntlProvider } from 'react-intl';
|
|
13
13
|
import HTMLEditor from '../HTMLEditor';
|
|
14
|
+
import { VALIDATION_SEVERITY } from '../constants';
|
|
15
|
+
import { ISSUE_SOURCES } from '../utils/validationConstants';
|
|
14
16
|
|
|
15
17
|
// Options to control CodeEditorPane mock behavior
|
|
16
18
|
const mockCodeEditorOptions = {
|
|
@@ -19,6 +21,7 @@ const mockCodeEditorOptions = {
|
|
|
19
21
|
setRef: true,
|
|
20
22
|
navigateToLineThrows: false,
|
|
21
23
|
includeNavigateToLine: true,
|
|
24
|
+
omitGetCursor: false,
|
|
22
25
|
};
|
|
23
26
|
|
|
24
27
|
|
|
@@ -166,11 +169,14 @@ jest.mock('../components/CodeEditorPane', () => {
|
|
|
166
169
|
|
|
167
170
|
const methods = {
|
|
168
171
|
focus: jest.fn(),
|
|
169
|
-
getCursor: jest.fn(() => 0),
|
|
170
172
|
getValue: jest.fn(() => value),
|
|
171
173
|
setValue: jest.fn((newValue) => setValue(newValue)),
|
|
172
174
|
};
|
|
173
175
|
|
|
176
|
+
if (!mockCodeEditorOptions.omitGetCursor) {
|
|
177
|
+
methods.getCursor = jest.fn(() => 0);
|
|
178
|
+
}
|
|
179
|
+
|
|
174
180
|
if (mockCodeEditorOptions.includeNavigateToLine) {
|
|
175
181
|
methods.navigateToLine = jest.fn(() => {
|
|
176
182
|
if (mockCodeEditorOptions.navigateToLineThrows) {
|
|
@@ -199,7 +205,20 @@ jest.mock('../components/CodeEditorPane', () => {
|
|
|
199
205
|
}
|
|
200
206
|
|
|
201
207
|
return (
|
|
202
|
-
<div
|
|
208
|
+
<div
|
|
209
|
+
data-testid="code-editor-pane"
|
|
210
|
+
data-tags={JSON.stringify(props.tags ?? null)}
|
|
211
|
+
data-injected-tags={JSON.stringify(props.injectedTags ?? null)}
|
|
212
|
+
data-location={JSON.stringify(props.location ?? null)}
|
|
213
|
+
data-event-context-tags={JSON.stringify(props.eventContextTags ?? null)}
|
|
214
|
+
data-wait-event-context-tags={JSON.stringify(props.waitEventContextTags ?? null)}
|
|
215
|
+
data-selected-offer-details={JSON.stringify(props.selectedOfferDetails ?? null)}
|
|
216
|
+
data-channel={JSON.stringify(props.channel ?? null)}
|
|
217
|
+
data-user-locale={props.userLocale ?? ''}
|
|
218
|
+
data-module-filter-enabled={String(!!props.moduleFilterEnabled)}
|
|
219
|
+
data-read-only={String(!!props.readOnly)}
|
|
220
|
+
data-is-fullscreen-mode={String(!!props.isFullscreenMode)}
|
|
221
|
+
>
|
|
203
222
|
<textarea
|
|
204
223
|
value={value}
|
|
205
224
|
onChange={(e) => {
|
|
@@ -216,6 +235,18 @@ jest.mock('../components/CodeEditorPane', () => {
|
|
|
216
235
|
>
|
|
217
236
|
Trigger Context Change
|
|
218
237
|
</button>
|
|
238
|
+
<button
|
|
239
|
+
onClick={() => props.onContextChange && props.onContextChange('ALL')}
|
|
240
|
+
data-testid="trigger-context-all"
|
|
241
|
+
>
|
|
242
|
+
Trigger Context ALL
|
|
243
|
+
</button>
|
|
244
|
+
<button
|
|
245
|
+
onClick={() => props.onContextChange && props.onContextChange(null)}
|
|
246
|
+
data-testid="trigger-context-null"
|
|
247
|
+
>
|
|
248
|
+
Trigger Context Null
|
|
249
|
+
</button>
|
|
219
250
|
{validation && (
|
|
220
251
|
<ValidationErrorDisplay
|
|
221
252
|
validation={validation}
|
|
@@ -249,6 +280,9 @@ jest.mock('../components/ValidationErrorDisplay', () => function MockValidationE
|
|
|
249
280
|
<button onClick={() => onErrorClick && onErrorClick({ line: null })}>
|
|
250
281
|
Error without Line
|
|
251
282
|
</button>
|
|
283
|
+
<button onClick={() => onErrorClick && onErrorClick(undefined)}>
|
|
284
|
+
Error undefined
|
|
285
|
+
</button>
|
|
252
286
|
</div>
|
|
253
287
|
);
|
|
254
288
|
});
|
|
@@ -384,6 +418,195 @@ describe('HTMLEditor', () => {
|
|
|
384
418
|
});
|
|
385
419
|
});
|
|
386
420
|
|
|
421
|
+
describe('waitEventContextTags', () => {
|
|
422
|
+
const waitTags = {
|
|
423
|
+
blockA: {
|
|
424
|
+
eventName: 'Order Placed',
|
|
425
|
+
blockName: 'Wait',
|
|
426
|
+
tags: [{ tagName: 'wait.foo', label: 'Foo', profileId: 'p1', profileName: 'P1' }],
|
|
427
|
+
},
|
|
428
|
+
};
|
|
429
|
+
|
|
430
|
+
it('passes default empty object to CodeEditorPane when waitEventContextTags is omitted', () => {
|
|
431
|
+
render(
|
|
432
|
+
<TestWrapper>
|
|
433
|
+
<HTMLEditor {...defaultProps} />
|
|
434
|
+
</TestWrapper>
|
|
435
|
+
);
|
|
436
|
+
|
|
437
|
+
act(() => {
|
|
438
|
+
jest.runAllTimers();
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
const pane = screen.getByTestId('code-editor-pane');
|
|
442
|
+
expect(pane).toHaveAttribute('data-wait-event-context-tags', JSON.stringify({}));
|
|
443
|
+
expect(pane).toHaveAttribute('data-is-fullscreen-mode', 'false');
|
|
444
|
+
});
|
|
445
|
+
|
|
446
|
+
it('forwards custom waitEventContextTags to the main CodeEditorPane', () => {
|
|
447
|
+
render(
|
|
448
|
+
<TestWrapper>
|
|
449
|
+
<HTMLEditor {...defaultProps} waitEventContextTags={waitTags} />
|
|
450
|
+
</TestWrapper>
|
|
451
|
+
);
|
|
452
|
+
|
|
453
|
+
act(() => {
|
|
454
|
+
jest.runAllTimers();
|
|
455
|
+
});
|
|
456
|
+
|
|
457
|
+
const pane = screen.getByTestId('code-editor-pane');
|
|
458
|
+
expect(pane).toHaveAttribute('data-wait-event-context-tags', JSON.stringify(waitTags));
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
it('forwards the same waitEventContextTags to fullscreen CodeEditorPane when modal opens', () => {
|
|
462
|
+
render(
|
|
463
|
+
<TestWrapper>
|
|
464
|
+
<HTMLEditor {...defaultProps} waitEventContextTags={waitTags} />
|
|
465
|
+
</TestWrapper>
|
|
466
|
+
);
|
|
467
|
+
|
|
468
|
+
act(() => {
|
|
469
|
+
jest.runAllTimers();
|
|
470
|
+
});
|
|
471
|
+
|
|
472
|
+
fireEvent.click(screen.getByText('Toggle Fullscreen'));
|
|
473
|
+
|
|
474
|
+
act(() => {
|
|
475
|
+
jest.runAllTimers();
|
|
476
|
+
});
|
|
477
|
+
|
|
478
|
+
const panes = screen.getAllByTestId('code-editor-pane');
|
|
479
|
+
expect(panes.length).toBeGreaterThanOrEqual(2);
|
|
480
|
+
const expected = JSON.stringify(waitTags);
|
|
481
|
+
panes.forEach((pane) => {
|
|
482
|
+
expect(pane).toHaveAttribute('data-wait-event-context-tags', expected);
|
|
483
|
+
});
|
|
484
|
+
const mainPane = panes.find((el) => el.getAttribute('data-is-fullscreen-mode') === 'false');
|
|
485
|
+
const fsPane = panes.find((el) => el.getAttribute('data-is-fullscreen-mode') === 'true');
|
|
486
|
+
expect(mainPane).toBeTruthy();
|
|
487
|
+
expect(fsPane).toBeTruthy();
|
|
488
|
+
});
|
|
489
|
+
|
|
490
|
+
it('forwards waitEventContextTags for inapp variant to main and fullscreen CodeEditorPane', () => {
|
|
491
|
+
render(
|
|
492
|
+
<TestWrapper>
|
|
493
|
+
<HTMLEditor {...defaultProps} variant="inapp" waitEventContextTags={waitTags} />
|
|
494
|
+
</TestWrapper>
|
|
495
|
+
);
|
|
496
|
+
|
|
497
|
+
act(() => {
|
|
498
|
+
jest.runAllTimers();
|
|
499
|
+
});
|
|
500
|
+
|
|
501
|
+
expect(screen.getByTestId('code-editor-pane')).toHaveAttribute(
|
|
502
|
+
'data-wait-event-context-tags',
|
|
503
|
+
JSON.stringify(waitTags),
|
|
504
|
+
);
|
|
505
|
+
|
|
506
|
+
fireEvent.click(screen.getByText('Toggle Fullscreen'));
|
|
507
|
+
|
|
508
|
+
act(() => {
|
|
509
|
+
jest.runAllTimers();
|
|
510
|
+
});
|
|
511
|
+
|
|
512
|
+
const panes = screen.getAllByTestId('code-editor-pane');
|
|
513
|
+
const expected = JSON.stringify(waitTags);
|
|
514
|
+
panes.forEach((pane) => {
|
|
515
|
+
expect(pane).toHaveAttribute('data-wait-event-context-tags', expected);
|
|
516
|
+
});
|
|
517
|
+
});
|
|
518
|
+
|
|
519
|
+
it('passes explicit null waitEventContextTags through to CodeEditorPane', () => {
|
|
520
|
+
render(
|
|
521
|
+
<TestWrapper>
|
|
522
|
+
<HTMLEditor {...defaultProps} waitEventContextTags={null} />
|
|
523
|
+
</TestWrapper>
|
|
524
|
+
);
|
|
525
|
+
|
|
526
|
+
act(() => {
|
|
527
|
+
jest.runAllTimers();
|
|
528
|
+
});
|
|
529
|
+
|
|
530
|
+
expect(screen.getByTestId('code-editor-pane')).toHaveAttribute(
|
|
531
|
+
'data-wait-event-context-tags',
|
|
532
|
+
JSON.stringify(null),
|
|
533
|
+
);
|
|
534
|
+
});
|
|
535
|
+
});
|
|
536
|
+
|
|
537
|
+
/**
|
|
538
|
+
* Covers forwardRef destructuring / defaults for tag-related props (HTMLEditor.js ~93–101)
|
|
539
|
+
* merged with HTMLEditor.defaultProps and passed through to CodeEditorPane (~663–671).
|
|
540
|
+
*/
|
|
541
|
+
describe('forwardRef tag-related props (HTMLEditor.js lines 93–101 → CodeEditorPane)', () => {
|
|
542
|
+
it('passes merged default tag-related props to CodeEditorPane when omitted on the instance', () => {
|
|
543
|
+
render(
|
|
544
|
+
<TestWrapper>
|
|
545
|
+
<HTMLEditor {...defaultProps} />
|
|
546
|
+
</TestWrapper>
|
|
547
|
+
);
|
|
548
|
+
|
|
549
|
+
act(() => {
|
|
550
|
+
jest.runAllTimers();
|
|
551
|
+
});
|
|
552
|
+
|
|
553
|
+
const pane = screen.getByTestId('code-editor-pane');
|
|
554
|
+
expect(pane).toHaveAttribute('data-tags', JSON.stringify([]));
|
|
555
|
+
expect(pane).toHaveAttribute('data-injected-tags', JSON.stringify({}));
|
|
556
|
+
expect(pane).toHaveAttribute('data-location', JSON.stringify(null));
|
|
557
|
+
expect(pane).toHaveAttribute('data-event-context-tags', JSON.stringify([]));
|
|
558
|
+
expect(pane).toHaveAttribute('data-wait-event-context-tags', JSON.stringify({}));
|
|
559
|
+
expect(pane).toHaveAttribute('data-selected-offer-details', JSON.stringify([]));
|
|
560
|
+
expect(pane).toHaveAttribute('data-channel', JSON.stringify(null));
|
|
561
|
+
expect(pane).toHaveAttribute('data-user-locale', 'en');
|
|
562
|
+
expect(pane).toHaveAttribute('data-module-filter-enabled', 'true');
|
|
563
|
+
expect(pane).toHaveAttribute('data-read-only', 'false');
|
|
564
|
+
});
|
|
565
|
+
|
|
566
|
+
it('forwards explicit tag-related props from the forwardRef parameter list to CodeEditorPane', () => {
|
|
567
|
+
const tags = [{ id: 't1' }];
|
|
568
|
+
const injectedTags = { custom: { name: 'Custom' } };
|
|
569
|
+
const location = { query: { module: 'outbound' } };
|
|
570
|
+
const eventContextTags = [{ tagName: 'entryTrigger.x' }];
|
|
571
|
+
const waitEventContextTags = { block1: { eventName: 'E', blockName: 'B', tags: [] } };
|
|
572
|
+
const selectedOfferDetails = [{ type: 'coupon' }];
|
|
573
|
+
|
|
574
|
+
render(
|
|
575
|
+
<TestWrapper>
|
|
576
|
+
<HTMLEditor
|
|
577
|
+
{...defaultProps}
|
|
578
|
+
tags={tags}
|
|
579
|
+
injectedTags={injectedTags}
|
|
580
|
+
location={location}
|
|
581
|
+
eventContextTags={eventContextTags}
|
|
582
|
+
waitEventContextTags={waitEventContextTags}
|
|
583
|
+
selectedOfferDetails={selectedOfferDetails}
|
|
584
|
+
channel="SMS"
|
|
585
|
+
userLocale="ja"
|
|
586
|
+
moduleFilterEnabled={false}
|
|
587
|
+
readOnly
|
|
588
|
+
/>
|
|
589
|
+
</TestWrapper>
|
|
590
|
+
);
|
|
591
|
+
|
|
592
|
+
act(() => {
|
|
593
|
+
jest.runAllTimers();
|
|
594
|
+
});
|
|
595
|
+
|
|
596
|
+
const pane = screen.getByTestId('code-editor-pane');
|
|
597
|
+
expect(pane).toHaveAttribute('data-tags', JSON.stringify(tags));
|
|
598
|
+
expect(pane).toHaveAttribute('data-injected-tags', JSON.stringify(injectedTags));
|
|
599
|
+
expect(pane).toHaveAttribute('data-location', JSON.stringify(location));
|
|
600
|
+
expect(pane).toHaveAttribute('data-event-context-tags', JSON.stringify(eventContextTags));
|
|
601
|
+
expect(pane).toHaveAttribute('data-wait-event-context-tags', JSON.stringify(waitEventContextTags));
|
|
602
|
+
expect(pane).toHaveAttribute('data-selected-offer-details', JSON.stringify(selectedOfferDetails));
|
|
603
|
+
expect(pane).toHaveAttribute('data-channel', JSON.stringify('SMS'));
|
|
604
|
+
expect(pane).toHaveAttribute('data-user-locale', 'ja');
|
|
605
|
+
expect(pane).toHaveAttribute('data-module-filter-enabled', 'false');
|
|
606
|
+
expect(pane).toHaveAttribute('data-read-only', 'true');
|
|
607
|
+
});
|
|
608
|
+
});
|
|
609
|
+
|
|
387
610
|
describe('Context Provider', () => {
|
|
388
611
|
it('provides correct context value for email variant', () => {
|
|
389
612
|
render(
|
|
@@ -1375,6 +1598,7 @@ describe('HTMLEditor', () => {
|
|
|
1375
1598
|
debounceMs: 500,
|
|
1376
1599
|
enableSanitization: true,
|
|
1377
1600
|
securityLevel: 'standard',
|
|
1601
|
+
isFullMode: true,
|
|
1378
1602
|
},
|
|
1379
1603
|
expect.any(Function),
|
|
1380
1604
|
expect.any(Function)
|
|
@@ -2478,6 +2702,7 @@ describe('HTMLEditor', () => {
|
|
|
2478
2702
|
mockCodeEditorOptions.setRef = true;
|
|
2479
2703
|
mockCodeEditorOptions.navigateToLineThrows = false;
|
|
2480
2704
|
mockCodeEditorOptions.includeNavigateToLine = true;
|
|
2705
|
+
mockCodeEditorOptions.omitGetCursor = false;
|
|
2481
2706
|
|
|
2482
2707
|
// Clear specific mocks instead of all to avoid breaking other mocks
|
|
2483
2708
|
CapNotification.warning.mockClear();
|
|
@@ -3201,7 +3426,6 @@ describe('HTMLEditor', () => {
|
|
|
3201
3426
|
onTagSelect={onTagSelect}
|
|
3202
3427
|
onContextChange={onContextChange}
|
|
3203
3428
|
globalActions={globalActions}
|
|
3204
|
-
isLiquidEnabled={true}
|
|
3205
3429
|
isFullMode={false}
|
|
3206
3430
|
onErrorAcknowledged={onErrorAcknowledged}
|
|
3207
3431
|
onValidationChange={onValidationChange}
|
|
@@ -3261,20 +3485,6 @@ describe('HTMLEditor', () => {
|
|
|
3261
3485
|
expect(screen.getByTestId('device-toggle')).toBeInTheDocument();
|
|
3262
3486
|
});
|
|
3263
3487
|
|
|
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
3488
|
it('should handle isFullMode prop', () => {
|
|
3279
3489
|
render(
|
|
3280
3490
|
<TestWrapper>
|
|
@@ -3481,6 +3691,7 @@ describe('HTMLEditor', () => {
|
|
|
3481
3691
|
tags={null}
|
|
3482
3692
|
injectedTags={null}
|
|
3483
3693
|
eventContextTags={null}
|
|
3694
|
+
waitEventContextTags={null}
|
|
3484
3695
|
selectedOfferDetails={null}
|
|
3485
3696
|
onTagContextChange={null}
|
|
3486
3697
|
onTagSelect={null}
|
|
@@ -3506,6 +3717,7 @@ describe('HTMLEditor', () => {
|
|
|
3506
3717
|
<HTMLEditor
|
|
3507
3718
|
tags={[]}
|
|
3508
3719
|
eventContextTags={[]}
|
|
3720
|
+
waitEventContextTags={{}}
|
|
3509
3721
|
selectedOfferDetails={[]}
|
|
3510
3722
|
/>
|
|
3511
3723
|
</TestWrapper>
|
|
@@ -3555,4 +3767,703 @@ describe('HTMLEditor', () => {
|
|
|
3555
3767
|
expect(screen.getByTestId('editor-toolbar')).toBeInTheDocument();
|
|
3556
3768
|
});
|
|
3557
3769
|
});
|
|
3770
|
+
|
|
3771
|
+
/**
|
|
3772
|
+
* Covers remaining HTMLEditor.js branches for 100% file coverage:
|
|
3773
|
+
* useImperativeHandle getters (302–304), validation effects (350, 416, 440–441), onErrorAcknowledged (543).
|
|
3774
|
+
*/
|
|
3775
|
+
describe('HTMLEditor full file coverage', () => {
|
|
3776
|
+
beforeEach(() => {
|
|
3777
|
+
mockCodeEditorOptions.omitGetCursor = false;
|
|
3778
|
+
mockCodeEditorOptions.setRef = true;
|
|
3779
|
+
mockCodeEditorOptions.includeInsertText = true;
|
|
3780
|
+
mockCodeEditorOptions.insertTextThrows = false;
|
|
3781
|
+
|
|
3782
|
+
mockUseValidationImpl.mockReset();
|
|
3783
|
+
mockUseValidationImpl.mockImplementation(() => ({
|
|
3784
|
+
isValidating: false,
|
|
3785
|
+
getAllIssues: () => [],
|
|
3786
|
+
isClean: () => true,
|
|
3787
|
+
summary: { totalErrors: 0, totalWarnings: 0 },
|
|
3788
|
+
}));
|
|
3789
|
+
|
|
3790
|
+
require('../hooks/useEditorContent').useEditorContent = () => ({
|
|
3791
|
+
content: '<p>Test content</p>',
|
|
3792
|
+
updateContent: jest.fn(),
|
|
3793
|
+
saveContent: jest.fn(),
|
|
3794
|
+
markAsSaved: jest.fn(),
|
|
3795
|
+
isLoading: false,
|
|
3796
|
+
isDirty: false,
|
|
3797
|
+
hasContent: true,
|
|
3798
|
+
getContentSize: jest.fn(() => 20),
|
|
3799
|
+
});
|
|
3800
|
+
|
|
3801
|
+
require('../hooks/useInAppContent').useInAppContent = () => ({
|
|
3802
|
+
content: '<p>Android content</p>',
|
|
3803
|
+
deviceContent: {
|
|
3804
|
+
android: '<p>Android content</p>',
|
|
3805
|
+
ios: '<p>iOS content</p>',
|
|
3806
|
+
},
|
|
3807
|
+
activeDevice: 'android',
|
|
3808
|
+
keepContentSame: false,
|
|
3809
|
+
updateContent: jest.fn(),
|
|
3810
|
+
saveContent: jest.fn(),
|
|
3811
|
+
markAsSaved: jest.fn(),
|
|
3812
|
+
switchDevice: jest.fn(),
|
|
3813
|
+
toggleContentSync: jest.fn(),
|
|
3814
|
+
getDeviceContent: (device) => `<p>${device} content</p>`,
|
|
3815
|
+
setDeviceContent: jest.fn(),
|
|
3816
|
+
getContentSize: () => 20,
|
|
3817
|
+
isLoading: false,
|
|
3818
|
+
isDirty: false,
|
|
3819
|
+
hasContent: true,
|
|
3820
|
+
});
|
|
3821
|
+
});
|
|
3822
|
+
|
|
3823
|
+
it('ref exposes getValidation, getContent, and isContentEmpty (lines 302–304)', async () => {
|
|
3824
|
+
const ref = React.createRef();
|
|
3825
|
+
const WrappedHTMLEditor = HTMLEditor.WrappedComponent || HTMLEditor;
|
|
3826
|
+
const intlProvider = new IntlProvider({ locale: 'en', messages: {} }, {});
|
|
3827
|
+
const { intl } = intlProvider.getChildContext();
|
|
3828
|
+
|
|
3829
|
+
mockUseValidationImpl.mockReturnValue({
|
|
3830
|
+
isValidating: false,
|
|
3831
|
+
getAllIssues: () => [],
|
|
3832
|
+
isClean: () => true,
|
|
3833
|
+
summary: { totalErrors: 0, totalWarnings: 0 },
|
|
3834
|
+
});
|
|
3835
|
+
|
|
3836
|
+
render(
|
|
3837
|
+
<TestWrapper>
|
|
3838
|
+
<WrappedHTMLEditor
|
|
3839
|
+
{...defaultProps}
|
|
3840
|
+
intl={intl}
|
|
3841
|
+
ref={ref}
|
|
3842
|
+
initialContent="<p>hi</p>"
|
|
3843
|
+
/>
|
|
3844
|
+
</TestWrapper>
|
|
3845
|
+
);
|
|
3846
|
+
|
|
3847
|
+
act(() => {
|
|
3848
|
+
jest.runAllTimers();
|
|
3849
|
+
});
|
|
3850
|
+
|
|
3851
|
+
await waitFor(() => {
|
|
3852
|
+
expect(ref.current).toBeTruthy();
|
|
3853
|
+
});
|
|
3854
|
+
|
|
3855
|
+
expect(ref.current.getValidation()).toBeDefined();
|
|
3856
|
+
expect(ref.current.getContent()).toBe('<p>Test content</p>');
|
|
3857
|
+
expect(ref.current.isContentEmpty()).toBe(false);
|
|
3858
|
+
});
|
|
3859
|
+
|
|
3860
|
+
it('skips main validation notify effect while isValidating is true (line 350)', () => {
|
|
3861
|
+
const onValidationChange = jest.fn();
|
|
3862
|
+
mockUseValidationImpl.mockReturnValue({
|
|
3863
|
+
isValidating: true,
|
|
3864
|
+
hasBlockingErrors: false,
|
|
3865
|
+
getAllIssues: () => [],
|
|
3866
|
+
isClean: () => true,
|
|
3867
|
+
summary: { totalErrors: 0, totalWarnings: 0 },
|
|
3868
|
+
});
|
|
3869
|
+
|
|
3870
|
+
render(
|
|
3871
|
+
<TestWrapper>
|
|
3872
|
+
<HTMLEditor {...defaultProps} onValidationChange={onValidationChange} />
|
|
3873
|
+
</TestWrapper>
|
|
3874
|
+
);
|
|
3875
|
+
|
|
3876
|
+
act(() => {
|
|
3877
|
+
jest.runAllTimers();
|
|
3878
|
+
});
|
|
3879
|
+
|
|
3880
|
+
expect(onValidationChange).toHaveBeenCalled();
|
|
3881
|
+
const withComplete = onValidationChange.mock.calls.filter((c) => c[0]?.validationComplete === true);
|
|
3882
|
+
expect(withComplete.length).toBe(0);
|
|
3883
|
+
});
|
|
3884
|
+
|
|
3885
|
+
it('skips api validation errors effect while validation is still running (line 416)', () => {
|
|
3886
|
+
const onValidationChange = jest.fn();
|
|
3887
|
+
mockUseValidationImpl.mockReturnValue({
|
|
3888
|
+
isValidating: true,
|
|
3889
|
+
hasBlockingErrors: false,
|
|
3890
|
+
getAllIssues: () => [],
|
|
3891
|
+
isClean: () => true,
|
|
3892
|
+
summary: { totalErrors: 0, totalWarnings: 0 },
|
|
3893
|
+
});
|
|
3894
|
+
|
|
3895
|
+
render(
|
|
3896
|
+
<TestWrapper>
|
|
3897
|
+
<HTMLEditor
|
|
3898
|
+
{...defaultProps}
|
|
3899
|
+
onValidationChange={onValidationChange}
|
|
3900
|
+
apiValidationErrors={{ liquidErrors: ['x'], standardErrors: [] }}
|
|
3901
|
+
/>
|
|
3902
|
+
</TestWrapper>
|
|
3903
|
+
);
|
|
3904
|
+
|
|
3905
|
+
act(() => {
|
|
3906
|
+
jest.runAllTimers();
|
|
3907
|
+
});
|
|
3908
|
+
|
|
3909
|
+
onValidationChange.mockClear();
|
|
3910
|
+
|
|
3911
|
+
act(() => {
|
|
3912
|
+
jest.runAllTimers();
|
|
3913
|
+
});
|
|
3914
|
+
|
|
3915
|
+
const afterClear = onValidationChange.mock.calls.filter((c) => c[0]?.validationComplete === true);
|
|
3916
|
+
expect(afterClear.length).toBe(0);
|
|
3917
|
+
});
|
|
3918
|
+
|
|
3919
|
+
it('api validation errors effect notifies parent when issue counts change (lines 440–441)', () => {
|
|
3920
|
+
const onValidationChange = jest.fn();
|
|
3921
|
+
let issues = [];
|
|
3922
|
+
|
|
3923
|
+
// Keep summary primitives stable so the main validation effect (line 343) does not re-run
|
|
3924
|
+
// when only getAllIssues() starts returning API-style issues; the api-only effect (409)
|
|
3925
|
+
// still runs because `validation` reference changes each render.
|
|
3926
|
+
mockUseValidationImpl.mockImplementation(() => ({
|
|
3927
|
+
isValidating: false,
|
|
3928
|
+
hasBlockingErrors: false,
|
|
3929
|
+
getAllIssues: () => issues,
|
|
3930
|
+
isClean: () => issues.length === 0,
|
|
3931
|
+
summary: { totalErrors: 0, totalWarnings: 0 },
|
|
3932
|
+
lastValidated: { getTime: () => 0 },
|
|
3933
|
+
}));
|
|
3934
|
+
|
|
3935
|
+
const { rerender } = render(
|
|
3936
|
+
<TestWrapper>
|
|
3937
|
+
<HTMLEditor {...defaultProps} onValidationChange={onValidationChange} />
|
|
3938
|
+
</TestWrapper>
|
|
3939
|
+
);
|
|
3940
|
+
|
|
3941
|
+
act(() => {
|
|
3942
|
+
jest.runAllTimers();
|
|
3943
|
+
});
|
|
3944
|
+
|
|
3945
|
+
onValidationChange.mockClear();
|
|
3946
|
+
|
|
3947
|
+
issues = [{ rule: 'liquid-api-validation', message: 'API error', source: 'liquid' }];
|
|
3948
|
+
|
|
3949
|
+
rerender(
|
|
3950
|
+
<TestWrapper>
|
|
3951
|
+
<HTMLEditor {...defaultProps} onValidationChange={onValidationChange} />
|
|
3952
|
+
</TestWrapper>
|
|
3953
|
+
);
|
|
3954
|
+
|
|
3955
|
+
act(() => {
|
|
3956
|
+
jest.runAllTimers();
|
|
3957
|
+
});
|
|
3958
|
+
|
|
3959
|
+
expect(onValidationChange).toHaveBeenCalled();
|
|
3960
|
+
const lastCall = onValidationChange.mock.calls[onValidationChange.mock.calls.length - 1][0];
|
|
3961
|
+
expect(lastCall.validationComplete).toBe(true);
|
|
3962
|
+
expect(lastCall.issueCounts.errors).toBeGreaterThanOrEqual(1);
|
|
3963
|
+
});
|
|
3964
|
+
|
|
3965
|
+
it('calls onErrorAcknowledged when user clicks a validation error (lines 542–543)', () => {
|
|
3966
|
+
const onErrorAcknowledged = jest.fn();
|
|
3967
|
+
mockUseValidationImpl.mockReturnValue({
|
|
3968
|
+
isValidating: false,
|
|
3969
|
+
getAllIssues: () => [{ source: 'htmlhint', rule: 'rule1', message: 'E' }],
|
|
3970
|
+
isClean: () => false,
|
|
3971
|
+
summary: { totalErrors: 1, totalWarnings: 0 },
|
|
3972
|
+
});
|
|
3973
|
+
|
|
3974
|
+
render(
|
|
3975
|
+
<TestWrapper>
|
|
3976
|
+
<HTMLEditor {...defaultProps} onErrorAcknowledged={onErrorAcknowledged} />
|
|
3977
|
+
</TestWrapper>
|
|
3978
|
+
);
|
|
3979
|
+
|
|
3980
|
+
act(() => {
|
|
3981
|
+
jest.runAllTimers();
|
|
3982
|
+
});
|
|
3983
|
+
|
|
3984
|
+
fireEvent.click(screen.getByText('Error without Line'));
|
|
3985
|
+
expect(onErrorAcknowledged).toHaveBeenCalled();
|
|
3986
|
+
});
|
|
3987
|
+
|
|
3988
|
+
it('getIssueCounts covers isBlockingError branches (standard API, liquid error, sanitizer, warning)', async () => {
|
|
3989
|
+
const ref = React.createRef();
|
|
3990
|
+
const WrappedHTMLEditor = HTMLEditor.WrappedComponent || HTMLEditor;
|
|
3991
|
+
const intlProvider = new IntlProvider({ locale: 'en', messages: {} }, {});
|
|
3992
|
+
const { intl } = intlProvider.getChildContext();
|
|
3993
|
+
|
|
3994
|
+
mockUseValidationImpl.mockReturnValue({
|
|
3995
|
+
isValidating: false,
|
|
3996
|
+
hasBlockingErrors: true,
|
|
3997
|
+
getAllIssues: () => [
|
|
3998
|
+
{ rule: 'standard-api-validation', message: 'a' },
|
|
3999
|
+
{ source: ISSUE_SOURCES.LIQUID, severity: VALIDATION_SEVERITY.ERROR, message: 'b' },
|
|
4000
|
+
{ rule: 'sanitizer.invalidInput', message: 'c' },
|
|
4001
|
+
{ rule: 'html-warn', message: 'd' },
|
|
4002
|
+
],
|
|
4003
|
+
isClean: () => false,
|
|
4004
|
+
summary: { totalErrors: 4, totalWarnings: 0 },
|
|
4005
|
+
});
|
|
4006
|
+
|
|
4007
|
+
render(
|
|
4008
|
+
<TestWrapper>
|
|
4009
|
+
<WrappedHTMLEditor {...defaultProps} intl={intl} ref={ref} />
|
|
4010
|
+
</TestWrapper>
|
|
4011
|
+
);
|
|
4012
|
+
|
|
4013
|
+
act(() => {
|
|
4014
|
+
jest.runAllTimers();
|
|
4015
|
+
});
|
|
4016
|
+
|
|
4017
|
+
await waitFor(() => {
|
|
4018
|
+
expect(ref.current).toBeTruthy();
|
|
4019
|
+
});
|
|
4020
|
+
|
|
4021
|
+
expect(ref.current.getIssueCounts()).toEqual({
|
|
4022
|
+
errors: 3,
|
|
4023
|
+
warnings: 1,
|
|
4024
|
+
total: 4,
|
|
4025
|
+
});
|
|
4026
|
+
});
|
|
4027
|
+
|
|
4028
|
+
it('maps ALL context to DEFAULT in handleContextChange', () => {
|
|
4029
|
+
const globalActions = { fetchSchemaForEntity: jest.fn() };
|
|
4030
|
+
const location = { query: { type: 'full' } };
|
|
4031
|
+
|
|
4032
|
+
render(
|
|
4033
|
+
<TestWrapper>
|
|
4034
|
+
<HTMLEditor
|
|
4035
|
+
{...defaultProps}
|
|
4036
|
+
globalActions={globalActions}
|
|
4037
|
+
location={location}
|
|
4038
|
+
variant="email"
|
|
4039
|
+
/>
|
|
4040
|
+
</TestWrapper>
|
|
4041
|
+
);
|
|
4042
|
+
|
|
4043
|
+
act(() => {
|
|
4044
|
+
jest.runAllTimers();
|
|
4045
|
+
});
|
|
4046
|
+
|
|
4047
|
+
fireEvent.click(screen.getByTestId('trigger-context-all'));
|
|
4048
|
+
|
|
4049
|
+
expect(globalActions.fetchSchemaForEntity).toHaveBeenCalledWith({
|
|
4050
|
+
layout: 'EMAIL',
|
|
4051
|
+
type: 'TAG',
|
|
4052
|
+
context: 'default',
|
|
4053
|
+
embedded: 'full',
|
|
4054
|
+
});
|
|
4055
|
+
});
|
|
4056
|
+
|
|
4057
|
+
it('email variant runs updateContent when initialContent differs from stored content', () => {
|
|
4058
|
+
const updateContent = jest.fn();
|
|
4059
|
+
require('../hooks/useEditorContent').useEditorContent = () => ({
|
|
4060
|
+
content: '<p>Stale</p>',
|
|
4061
|
+
updateContent,
|
|
4062
|
+
saveContent: jest.fn(),
|
|
4063
|
+
markAsSaved: jest.fn(),
|
|
4064
|
+
isLoading: false,
|
|
4065
|
+
isDirty: false,
|
|
4066
|
+
hasContent: true,
|
|
4067
|
+
getContentSize: jest.fn(() => 20),
|
|
4068
|
+
});
|
|
4069
|
+
|
|
4070
|
+
render(
|
|
4071
|
+
<TestWrapper>
|
|
4072
|
+
<HTMLEditor {...defaultProps} initialContent="<p>Initial content</p>" />
|
|
4073
|
+
</TestWrapper>
|
|
4074
|
+
);
|
|
4075
|
+
|
|
4076
|
+
act(() => {
|
|
4077
|
+
jest.runAllTimers();
|
|
4078
|
+
});
|
|
4079
|
+
|
|
4080
|
+
expect(updateContent).toHaveBeenCalledWith('<p>Initial content</p>', true);
|
|
4081
|
+
});
|
|
4082
|
+
|
|
4083
|
+
it('inapp variant runs updateContent when device content differs from initialContent string', () => {
|
|
4084
|
+
const updateContent = jest.fn();
|
|
4085
|
+
require('../hooks/useInAppContent').useInAppContent = () => ({
|
|
4086
|
+
activeDevice: 'android',
|
|
4087
|
+
keepContentSame: false,
|
|
4088
|
+
updateContent,
|
|
4089
|
+
saveContent: jest.fn(),
|
|
4090
|
+
markAsSaved: jest.fn(),
|
|
4091
|
+
switchDevice: jest.fn(),
|
|
4092
|
+
toggleContentSync: jest.fn(),
|
|
4093
|
+
getDeviceContent: () => '<p>android content</p>',
|
|
4094
|
+
setDeviceContent: jest.fn(),
|
|
4095
|
+
getContentSize: () => 20,
|
|
4096
|
+
isLoading: false,
|
|
4097
|
+
isDirty: false,
|
|
4098
|
+
hasContent: true,
|
|
4099
|
+
});
|
|
4100
|
+
|
|
4101
|
+
render(
|
|
4102
|
+
<TestWrapper>
|
|
4103
|
+
<HTMLEditor
|
|
4104
|
+
{...defaultProps}
|
|
4105
|
+
variant="inapp"
|
|
4106
|
+
initialContent="<p>Initial content</p>"
|
|
4107
|
+
/>
|
|
4108
|
+
</TestWrapper>
|
|
4109
|
+
);
|
|
4110
|
+
|
|
4111
|
+
act(() => {
|
|
4112
|
+
jest.runAllTimers();
|
|
4113
|
+
});
|
|
4114
|
+
|
|
4115
|
+
expect(updateContent).toHaveBeenCalledWith('<p>Initial content</p>', true);
|
|
4116
|
+
});
|
|
4117
|
+
|
|
4118
|
+
it('handleLabelInsert uses numeric cursor when getCursor is not a function', () => {
|
|
4119
|
+
const CapNotification = require('@capillarytech/cap-ui-library/CapNotification');
|
|
4120
|
+
mockCodeEditorOptions.omitGetCursor = true;
|
|
4121
|
+
|
|
4122
|
+
render(
|
|
4123
|
+
<TestWrapper>
|
|
4124
|
+
<HTMLEditor {...defaultProps} />
|
|
4125
|
+
</TestWrapper>
|
|
4126
|
+
);
|
|
4127
|
+
|
|
4128
|
+
act(() => {
|
|
4129
|
+
jest.runAllTimers();
|
|
4130
|
+
});
|
|
4131
|
+
|
|
4132
|
+
fireEvent.click(screen.getByText('Insert Label (Null Position)'));
|
|
4133
|
+
|
|
4134
|
+
expect(CapNotification.success).toHaveBeenCalled();
|
|
4135
|
+
});
|
|
4136
|
+
|
|
4137
|
+
it('handleValidationErrorClick with undefined error focuses editor when ref is set', () => {
|
|
4138
|
+
mockUseValidationImpl.mockReturnValue({
|
|
4139
|
+
isValidating: false,
|
|
4140
|
+
getAllIssues: () => [{ source: 'htmlhint', rule: 'rule1', message: 'E' }],
|
|
4141
|
+
isClean: () => false,
|
|
4142
|
+
summary: { totalErrors: 1, totalWarnings: 0 },
|
|
4143
|
+
});
|
|
4144
|
+
|
|
4145
|
+
render(
|
|
4146
|
+
<TestWrapper>
|
|
4147
|
+
<HTMLEditor {...defaultProps} />
|
|
4148
|
+
</TestWrapper>
|
|
4149
|
+
);
|
|
4150
|
+
|
|
4151
|
+
act(() => {
|
|
4152
|
+
jest.runAllTimers();
|
|
4153
|
+
});
|
|
4154
|
+
|
|
4155
|
+
fireEvent.click(screen.getByText('Error undefined'));
|
|
4156
|
+
});
|
|
4157
|
+
|
|
4158
|
+
it('handleValidationErrorClick skips editor when ref is not available', () => {
|
|
4159
|
+
mockCodeEditorOptions.setRef = false;
|
|
4160
|
+
mockUseValidationImpl.mockReturnValue({
|
|
4161
|
+
isValidating: false,
|
|
4162
|
+
getAllIssues: () => [{ source: 'htmlhint', rule: 'rule1', message: 'E' }],
|
|
4163
|
+
isClean: () => false,
|
|
4164
|
+
summary: { totalErrors: 1, totalWarnings: 0 },
|
|
4165
|
+
});
|
|
4166
|
+
|
|
4167
|
+
render(
|
|
4168
|
+
<TestWrapper>
|
|
4169
|
+
<HTMLEditor {...defaultProps} onErrorAcknowledged={jest.fn()} />
|
|
4170
|
+
</TestWrapper>
|
|
4171
|
+
);
|
|
4172
|
+
|
|
4173
|
+
act(() => {
|
|
4174
|
+
jest.runAllTimers();
|
|
4175
|
+
});
|
|
4176
|
+
|
|
4177
|
+
fireEvent.click(screen.getByText('Error at Line 5'));
|
|
4178
|
+
});
|
|
4179
|
+
|
|
4180
|
+
it('getIssueCounts treats nullish issues as non-blocking (isBlockingError issue || {})', async () => {
|
|
4181
|
+
const ref = React.createRef();
|
|
4182
|
+
const WrappedHTMLEditor = HTMLEditor.WrappedComponent || HTMLEditor;
|
|
4183
|
+
const intlProvider = new IntlProvider({ locale: 'en', messages: {} }, {});
|
|
4184
|
+
const { intl } = intlProvider.getChildContext();
|
|
4185
|
+
|
|
4186
|
+
mockUseValidationImpl.mockReturnValue({
|
|
4187
|
+
isValidating: false,
|
|
4188
|
+
hasBlockingErrors: false,
|
|
4189
|
+
getAllIssues: () => [undefined, null, {}],
|
|
4190
|
+
isClean: () => false,
|
|
4191
|
+
summary: { totalErrors: 0, totalWarnings: 3 },
|
|
4192
|
+
});
|
|
4193
|
+
|
|
4194
|
+
render(
|
|
4195
|
+
<TestWrapper>
|
|
4196
|
+
<WrappedHTMLEditor {...defaultProps} intl={intl} ref={ref} />
|
|
4197
|
+
</TestWrapper>
|
|
4198
|
+
);
|
|
4199
|
+
|
|
4200
|
+
act(() => {
|
|
4201
|
+
jest.runAllTimers();
|
|
4202
|
+
});
|
|
4203
|
+
|
|
4204
|
+
await waitFor(() => {
|
|
4205
|
+
expect(ref.current).toBeTruthy();
|
|
4206
|
+
});
|
|
4207
|
+
|
|
4208
|
+
expect(ref.current.getIssueCounts()).toEqual({
|
|
4209
|
+
errors: 0,
|
|
4210
|
+
warnings: 3,
|
|
4211
|
+
total: 3,
|
|
4212
|
+
});
|
|
4213
|
+
});
|
|
4214
|
+
|
|
4215
|
+
it('handleContextChange uses embedded full when location.query is missing', () => {
|
|
4216
|
+
const globalActions = { fetchSchemaForEntity: jest.fn() };
|
|
4217
|
+
const location = { pathname: '/creatives' };
|
|
4218
|
+
|
|
4219
|
+
render(
|
|
4220
|
+
<TestWrapper>
|
|
4221
|
+
<HTMLEditor
|
|
4222
|
+
{...defaultProps}
|
|
4223
|
+
globalActions={globalActions}
|
|
4224
|
+
location={location}
|
|
4225
|
+
variant="email"
|
|
4226
|
+
/>
|
|
4227
|
+
</TestWrapper>
|
|
4228
|
+
);
|
|
4229
|
+
|
|
4230
|
+
act(() => {
|
|
4231
|
+
jest.runAllTimers();
|
|
4232
|
+
});
|
|
4233
|
+
|
|
4234
|
+
fireEvent.click(screen.getByTestId('trigger-context-change'));
|
|
4235
|
+
|
|
4236
|
+
expect(globalActions.fetchSchemaForEntity).toHaveBeenCalledWith({
|
|
4237
|
+
layout: 'EMAIL',
|
|
4238
|
+
type: 'TAG',
|
|
4239
|
+
context: 'test-context',
|
|
4240
|
+
embedded: 'full',
|
|
4241
|
+
});
|
|
4242
|
+
});
|
|
4243
|
+
|
|
4244
|
+
it('handleSave succeeds when content.content is undefined', () => {
|
|
4245
|
+
const CapNotification = require('@capillarytech/cap-ui-library/CapNotification');
|
|
4246
|
+
const onSave = jest.fn();
|
|
4247
|
+
|
|
4248
|
+
require('../hooks/useEditorContent').useEditorContent = () => ({
|
|
4249
|
+
content: undefined,
|
|
4250
|
+
updateContent: jest.fn(),
|
|
4251
|
+
saveContent: jest.fn(),
|
|
4252
|
+
markAsSaved: jest.fn(),
|
|
4253
|
+
isLoading: false,
|
|
4254
|
+
isDirty: false,
|
|
4255
|
+
hasContent: false,
|
|
4256
|
+
getContentSize: jest.fn(() => 0),
|
|
4257
|
+
});
|
|
4258
|
+
|
|
4259
|
+
render(
|
|
4260
|
+
<TestWrapper>
|
|
4261
|
+
<HTMLEditor {...defaultProps} onSave={onSave} />
|
|
4262
|
+
</TestWrapper>
|
|
4263
|
+
);
|
|
4264
|
+
|
|
4265
|
+
act(() => {
|
|
4266
|
+
jest.runAllTimers();
|
|
4267
|
+
});
|
|
4268
|
+
|
|
4269
|
+
fireEvent.click(screen.getByText('Save'));
|
|
4270
|
+
|
|
4271
|
+
expect(onSave).toHaveBeenCalledWith({ html: undefined, css: undefined, javascript: undefined });
|
|
4272
|
+
expect(CapNotification.success).toHaveBeenCalled();
|
|
4273
|
+
});
|
|
4274
|
+
|
|
4275
|
+
it('countIssuesBySeverity handles null allIssues from getAllIssues', async () => {
|
|
4276
|
+
const ref = React.createRef();
|
|
4277
|
+
const WrappedHTMLEditor = HTMLEditor.WrappedComponent || HTMLEditor;
|
|
4278
|
+
const intlProvider = new IntlProvider({ locale: 'en', messages: {} }, {});
|
|
4279
|
+
const { intl } = intlProvider.getChildContext();
|
|
4280
|
+
|
|
4281
|
+
mockUseValidationImpl.mockReturnValue({
|
|
4282
|
+
isValidating: false,
|
|
4283
|
+
getAllIssues: () => null,
|
|
4284
|
+
isClean: () => true,
|
|
4285
|
+
summary: { totalErrors: 0, totalWarnings: 0 },
|
|
4286
|
+
});
|
|
4287
|
+
|
|
4288
|
+
render(
|
|
4289
|
+
<TestWrapper>
|
|
4290
|
+
<WrappedHTMLEditor {...defaultProps} intl={intl} ref={ref} />
|
|
4291
|
+
</TestWrapper>
|
|
4292
|
+
);
|
|
4293
|
+
|
|
4294
|
+
act(() => {
|
|
4295
|
+
jest.runAllTimers();
|
|
4296
|
+
});
|
|
4297
|
+
|
|
4298
|
+
await waitFor(() => {
|
|
4299
|
+
expect(ref.current).toBeTruthy();
|
|
4300
|
+
});
|
|
4301
|
+
|
|
4302
|
+
expect(ref.current.getIssueCounts()).toEqual({
|
|
4303
|
+
errors: 0,
|
|
4304
|
+
warnings: 0,
|
|
4305
|
+
total: 0,
|
|
4306
|
+
});
|
|
4307
|
+
});
|
|
4308
|
+
|
|
4309
|
+
it('handleContextChange lowercases null contextData to empty string', () => {
|
|
4310
|
+
const globalActions = { fetchSchemaForEntity: jest.fn() };
|
|
4311
|
+
const location = { query: { type: 'full' } };
|
|
4312
|
+
|
|
4313
|
+
render(
|
|
4314
|
+
<TestWrapper>
|
|
4315
|
+
<HTMLEditor
|
|
4316
|
+
{...defaultProps}
|
|
4317
|
+
globalActions={globalActions}
|
|
4318
|
+
location={location}
|
|
4319
|
+
variant="email"
|
|
4320
|
+
/>
|
|
4321
|
+
</TestWrapper>
|
|
4322
|
+
);
|
|
4323
|
+
|
|
4324
|
+
act(() => {
|
|
4325
|
+
jest.runAllTimers();
|
|
4326
|
+
});
|
|
4327
|
+
|
|
4328
|
+
fireEvent.click(screen.getByTestId('trigger-context-null'));
|
|
4329
|
+
|
|
4330
|
+
expect(globalActions.fetchSchemaForEntity).toHaveBeenCalledWith({
|
|
4331
|
+
layout: 'EMAIL',
|
|
4332
|
+
type: 'TAG',
|
|
4333
|
+
context: '',
|
|
4334
|
+
embedded: 'full',
|
|
4335
|
+
});
|
|
4336
|
+
});
|
|
4337
|
+
|
|
4338
|
+
it('inapp initialContent object falls back to ANDROID when active device slot is missing', () => {
|
|
4339
|
+
const updateContent = jest.fn();
|
|
4340
|
+
require('../hooks/useInAppContent').useInAppContent = () => ({
|
|
4341
|
+
activeDevice: 'android',
|
|
4342
|
+
keepContentSame: false,
|
|
4343
|
+
updateContent,
|
|
4344
|
+
saveContent: jest.fn(),
|
|
4345
|
+
markAsSaved: jest.fn(),
|
|
4346
|
+
switchDevice: jest.fn(),
|
|
4347
|
+
toggleContentSync: jest.fn(),
|
|
4348
|
+
getDeviceContent: () => '<p>old</p>',
|
|
4349
|
+
setDeviceContent: jest.fn(),
|
|
4350
|
+
getContentSize: () => 20,
|
|
4351
|
+
isLoading: false,
|
|
4352
|
+
isDirty: false,
|
|
4353
|
+
hasContent: true,
|
|
4354
|
+
});
|
|
4355
|
+
|
|
4356
|
+
render(
|
|
4357
|
+
<TestWrapper>
|
|
4358
|
+
<HTMLEditor
|
|
4359
|
+
{...defaultProps}
|
|
4360
|
+
variant="inapp"
|
|
4361
|
+
initialContent={{ ios: '<p>ios only</p>' }}
|
|
4362
|
+
/>
|
|
4363
|
+
</TestWrapper>
|
|
4364
|
+
);
|
|
4365
|
+
|
|
4366
|
+
act(() => {
|
|
4367
|
+
jest.runAllTimers();
|
|
4368
|
+
});
|
|
4369
|
+
|
|
4370
|
+
expect(updateContent).toHaveBeenCalledWith('', true);
|
|
4371
|
+
});
|
|
4372
|
+
|
|
4373
|
+
it('inapp initialContent effect skips when updateContent is not available', () => {
|
|
4374
|
+
require('../hooks/useInAppContent').useInAppContent = () => ({
|
|
4375
|
+
activeDevice: 'android',
|
|
4376
|
+
keepContentSame: false,
|
|
4377
|
+
updateContent: undefined,
|
|
4378
|
+
saveContent: jest.fn(),
|
|
4379
|
+
markAsSaved: jest.fn(),
|
|
4380
|
+
switchDevice: jest.fn(),
|
|
4381
|
+
toggleContentSync: jest.fn(),
|
|
4382
|
+
getDeviceContent: () => '<p>x</p>',
|
|
4383
|
+
setDeviceContent: jest.fn(),
|
|
4384
|
+
getContentSize: () => 20,
|
|
4385
|
+
isLoading: false,
|
|
4386
|
+
isDirty: false,
|
|
4387
|
+
hasContent: true,
|
|
4388
|
+
});
|
|
4389
|
+
|
|
4390
|
+
render(
|
|
4391
|
+
<TestWrapper>
|
|
4392
|
+
<HTMLEditor
|
|
4393
|
+
{...defaultProps}
|
|
4394
|
+
variant="inapp"
|
|
4395
|
+
initialContent="<p>y</p>"
|
|
4396
|
+
/>
|
|
4397
|
+
</TestWrapper>
|
|
4398
|
+
);
|
|
4399
|
+
|
|
4400
|
+
act(() => {
|
|
4401
|
+
jest.runAllTimers();
|
|
4402
|
+
});
|
|
4403
|
+
|
|
4404
|
+
expect(screen.getByTestId('device-toggle')).toBeInTheDocument();
|
|
4405
|
+
});
|
|
4406
|
+
|
|
4407
|
+
it('uses default forwardRef props when only intl is supplied', () => {
|
|
4408
|
+
const WrappedHTMLEditor = HTMLEditor.WrappedComponent || HTMLEditor;
|
|
4409
|
+
const intlProvider = new IntlProvider({ locale: 'en', messages: {} }, {});
|
|
4410
|
+
const { intl } = intlProvider.getChildContext();
|
|
4411
|
+
|
|
4412
|
+
render(
|
|
4413
|
+
<TestWrapper>
|
|
4414
|
+
<WrappedHTMLEditor intl={intl} />
|
|
4415
|
+
</TestWrapper>
|
|
4416
|
+
);
|
|
4417
|
+
|
|
4418
|
+
act(() => {
|
|
4419
|
+
jest.runAllTimers();
|
|
4420
|
+
});
|
|
4421
|
+
|
|
4422
|
+
expect(screen.getByTestId('editor-toolbar')).toBeInTheDocument();
|
|
4423
|
+
});
|
|
4424
|
+
|
|
4425
|
+
it('main validation effect notifies when isContentEmpty changes (hasChanged branch)', () => {
|
|
4426
|
+
const onValidationChange = jest.fn();
|
|
4427
|
+
let hookContent = '';
|
|
4428
|
+
|
|
4429
|
+
require('../hooks/useEditorContent').useEditorContent = () => ({
|
|
4430
|
+
content: hookContent,
|
|
4431
|
+
updateContent: jest.fn(),
|
|
4432
|
+
saveContent: jest.fn(),
|
|
4433
|
+
markAsSaved: jest.fn(),
|
|
4434
|
+
isLoading: false,
|
|
4435
|
+
isDirty: false,
|
|
4436
|
+
hasContent: !!hookContent,
|
|
4437
|
+
getContentSize: jest.fn(() => hookContent.length),
|
|
4438
|
+
});
|
|
4439
|
+
|
|
4440
|
+
const { rerender } = render(
|
|
4441
|
+
<TestWrapper>
|
|
4442
|
+
<HTMLEditor {...defaultProps} onValidationChange={onValidationChange} />
|
|
4443
|
+
</TestWrapper>
|
|
4444
|
+
);
|
|
4445
|
+
|
|
4446
|
+
act(() => {
|
|
4447
|
+
jest.runAllTimers();
|
|
4448
|
+
});
|
|
4449
|
+
|
|
4450
|
+
onValidationChange.mockClear();
|
|
4451
|
+
|
|
4452
|
+
hookContent = '<p>not empty</p>';
|
|
4453
|
+
|
|
4454
|
+
rerender(
|
|
4455
|
+
<TestWrapper>
|
|
4456
|
+
<HTMLEditor {...defaultProps} onValidationChange={onValidationChange} />
|
|
4457
|
+
</TestWrapper>
|
|
4458
|
+
);
|
|
4459
|
+
|
|
4460
|
+
act(() => {
|
|
4461
|
+
jest.runAllTimers();
|
|
4462
|
+
});
|
|
4463
|
+
|
|
4464
|
+
expect(onValidationChange).toHaveBeenCalled();
|
|
4465
|
+
const lastCall = onValidationChange.mock.calls[onValidationChange.mock.calls.length - 1][0];
|
|
4466
|
+
expect(lastCall.isContentEmpty).toBe(false);
|
|
4467
|
+
});
|
|
4468
|
+
});
|
|
3558
4469
|
});
|