@capillarytech/creatives-library 8.0.271 → 8.0.273
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 +2 -1
- package/initialReducer.js +2 -0
- package/package.json +1 -1
- package/services/api.js +10 -0
- package/services/tests/api.test.js +34 -0
- package/tests/integration/TemplateCreation/TemplateCreation.integration.test.js +17 -35
- package/tests/integration/TemplateCreation/api-response.js +31 -1
- package/tests/integration/TemplateCreation/msw-handler.js +2 -0
- package/utils/common.js +5 -0
- package/utils/commonUtils.js +28 -5
- package/utils/imageUrlUpload.js +13 -14
- package/utils/tests/commonUtil.test.js +224 -0
- package/utils/tests/imageUrlUpload.test.js +298 -0
- package/utils/transformTemplateConfig.js +0 -10
- package/v2Components/CapDeviceContent/index.js +61 -56
- package/v2Components/CapTagList/index.js +6 -1
- package/v2Components/CapTagListWithInput/index.js +5 -1
- package/v2Components/CapTagListWithInput/messages.js +1 -1
- package/v2Components/CapWhatsappCTA/tests/index.test.js +5 -0
- package/v2Components/ErrorInfoNote/constants.js +1 -0
- package/v2Components/ErrorInfoNote/index.js +402 -72
- package/v2Components/ErrorInfoNote/messages.js +32 -6
- package/v2Components/ErrorInfoNote/style.scss +278 -6
- package/v2Components/FormBuilder/tests/index.test.js +13 -4
- package/v2Components/HtmlEditor/HTMLEditor.js +418 -99
- package/v2Components/HtmlEditor/__tests__/HTMLEditor.apiErrors.test.js +870 -0
- package/v2Components/HtmlEditor/__tests__/HTMLEditor.test.js +1882 -133
- package/v2Components/HtmlEditor/__tests__/index.lazy.test.js +27 -16
- package/v2Components/HtmlEditor/_htmlEditor.scss +108 -45
- package/v2Components/HtmlEditor/_index.lazy.scss +0 -1
- package/v2Components/HtmlEditor/components/CodeEditorPane/_codeEditorPane.scss +23 -102
- package/v2Components/HtmlEditor/components/CodeEditorPane/index.js +148 -140
- package/v2Components/HtmlEditor/components/DeviceToggle/_deviceToggle.scss +2 -1
- package/v2Components/HtmlEditor/components/DeviceToggle/index.js +3 -3
- package/v2Components/HtmlEditor/components/EditorToolbar/_editorToolbar.scss +9 -1
- package/v2Components/HtmlEditor/components/EditorToolbar/index.js +31 -6
- package/v2Components/HtmlEditor/components/FullscreenModal/_fullscreenModal.scss +22 -0
- package/v2Components/HtmlEditor/components/InAppPreviewPane/DeviceFrame.js +4 -7
- package/v2Components/HtmlEditor/components/InAppPreviewPane/__tests__/DeviceFrame.test.js +35 -45
- package/v2Components/HtmlEditor/components/InAppPreviewPane/_inAppPreviewPane.scss +1 -3
- package/v2Components/HtmlEditor/components/InAppPreviewPane/constants.js +33 -33
- package/v2Components/HtmlEditor/components/InAppPreviewPane/index.js +7 -6
- package/v2Components/HtmlEditor/components/PreviewPane/_previewPane.scss +7 -10
- package/v2Components/HtmlEditor/components/PreviewPane/index.js +22 -43
- package/v2Components/HtmlEditor/components/SplitContainer/_splitContainer.scss +1 -1
- package/v2Components/HtmlEditor/components/ValidationErrorDisplay/_validationErrorDisplay.scss +18 -0
- package/v2Components/HtmlEditor/components/ValidationErrorDisplay/index.js +36 -31
- package/v2Components/HtmlEditor/components/ValidationPanel/_validationPanel.scss +46 -34
- package/v2Components/HtmlEditor/components/ValidationPanel/constants.js +6 -0
- package/v2Components/HtmlEditor/components/ValidationPanel/index.js +52 -46
- package/v2Components/HtmlEditor/components/ValidationTabs/_validationTabs.scss +277 -0
- package/v2Components/HtmlEditor/components/ValidationTabs/index.js +295 -0
- package/v2Components/HtmlEditor/components/ValidationTabs/messages.js +51 -0
- package/v2Components/HtmlEditor/constants.js +45 -20
- package/v2Components/HtmlEditor/hooks/__tests__/useInAppContent.test.js +373 -16
- package/v2Components/HtmlEditor/hooks/__tests__/useValidation.test.js +351 -16
- package/v2Components/HtmlEditor/hooks/useEditorContent.js +5 -2
- package/v2Components/HtmlEditor/hooks/useInAppContent.js +88 -146
- package/v2Components/HtmlEditor/hooks/useValidation.js +213 -56
- package/v2Components/HtmlEditor/index.js +1 -1
- package/v2Components/HtmlEditor/messages.js +102 -94
- package/v2Components/HtmlEditor/utils/__tests__/htmlValidator.enhanced.test.js +214 -45
- package/v2Components/HtmlEditor/utils/__tests__/validationAdapter.test.js +134 -0
- package/v2Components/HtmlEditor/utils/contentSanitizer.js +40 -41
- package/v2Components/HtmlEditor/utils/htmlValidator.js +71 -72
- package/v2Components/HtmlEditor/utils/liquidTemplateSupport.js +158 -124
- package/v2Components/HtmlEditor/utils/properSyntaxHighlighting.js +23 -25
- package/v2Components/HtmlEditor/utils/validationAdapter.js +66 -41
- package/v2Components/HtmlEditor/utils/validationConstants.js +38 -0
- package/v2Components/MobilePushPreviewV2/constants.js +6 -0
- package/v2Components/MobilePushPreviewV2/index.js +33 -7
- package/v2Components/TemplatePreview/_templatePreview.scss +55 -24
- package/v2Components/TemplatePreview/index.js +47 -32
- package/v2Components/TemplatePreview/messages.js +4 -0
- package/v2Components/TestAndPreviewSlidebox/_testAndPreviewSlidebox.scss +1 -0
- package/v2Containers/BeeEditor/index.js +172 -90
- package/v2Containers/BeePopupEditor/_beePopupEditor.scss +14 -0
- package/v2Containers/BeePopupEditor/constants.js +10 -0
- package/v2Containers/BeePopupEditor/index.js +194 -0
- package/v2Containers/BeePopupEditor/tests/index.test.js +627 -0
- package/v2Containers/CreativesContainer/SlideBoxContent.js +127 -51
- package/v2Containers/CreativesContainer/SlideBoxFooter.js +156 -13
- package/v2Containers/CreativesContainer/SlideBoxHeader.js +2 -1
- package/v2Containers/CreativesContainer/constants.js +1 -0
- package/v2Containers/CreativesContainer/index.js +251 -47
- package/v2Containers/CreativesContainer/messages.js +8 -0
- package/v2Containers/CreativesContainer/tests/SlideBoxFooter.test.js +11 -2
- package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +38 -50
- package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +103 -0
- package/v2Containers/Email/actions.js +7 -0
- package/v2Containers/Email/constants.js +5 -1
- package/v2Containers/Email/index.js +234 -29
- package/v2Containers/Email/messages.js +32 -0
- package/v2Containers/Email/reducer.js +12 -1
- package/v2Containers/Email/sagas.js +61 -7
- package/v2Containers/Email/tests/__snapshots__/reducer.test.js.snap +2 -0
- package/v2Containers/Email/tests/reducer.test.js +46 -0
- package/v2Containers/Email/tests/sagas.test.js +320 -29
- package/v2Containers/EmailWrapper/components/EmailHTMLEditor.js +1246 -0
- package/v2Containers/EmailWrapper/components/EmailWrapperView.js +212 -21
- package/v2Containers/EmailWrapper/components/HTMLEditorTesting.js +40 -74
- package/v2Containers/EmailWrapper/components/__tests__/EmailHTMLEditor.test.js +2614 -0
- package/v2Containers/EmailWrapper/components/__tests__/EmailWrapperView.test.js +520 -0
- package/v2Containers/EmailWrapper/components/__tests__/HTMLEditorTesting.test.js +2 -67
- package/v2Containers/EmailWrapper/constants.js +2 -0
- package/v2Containers/EmailWrapper/hooks/useEmailWrapper.js +627 -79
- package/v2Containers/EmailWrapper/index.js +103 -23
- package/v2Containers/EmailWrapper/messages.js +65 -1
- package/v2Containers/EmailWrapper/tests/useEmailWrapper.edgeCases.test.js +955 -0
- package/v2Containers/EmailWrapper/tests/useEmailWrapper.test.js +596 -82
- package/v2Containers/InApp/__tests__/InAppHTMLEditor.test.js +376 -0
- package/v2Containers/InApp/__tests__/sagas.test.js +363 -0
- package/v2Containers/InApp/actions.js +7 -0
- package/v2Containers/InApp/constants.js +20 -4
- package/v2Containers/InApp/index.js +802 -360
- package/v2Containers/InApp/index.scss +4 -3
- package/v2Containers/InApp/messages.js +7 -3
- package/v2Containers/InApp/reducer.js +21 -3
- package/v2Containers/InApp/sagas.js +29 -9
- package/v2Containers/InApp/selectors.js +25 -5
- package/v2Containers/InApp/tests/index.test.js +154 -50
- package/v2Containers/InApp/tests/reducer.test.js +34 -0
- package/v2Containers/InApp/tests/sagas.test.js +61 -9
- package/v2Containers/InApp/tests/selectors.test.js +612 -0
- package/v2Containers/InAppWrapper/components/InAppWrapperView.js +151 -0
- package/v2Containers/InAppWrapper/components/__tests__/InAppWrapperView.test.js +267 -0
- package/v2Containers/InAppWrapper/components/inAppWrapperView.scss +23 -0
- package/v2Containers/InAppWrapper/constants.js +16 -0
- package/v2Containers/InAppWrapper/hooks/__tests__/useInAppWrapper.test.js +473 -0
- package/v2Containers/InAppWrapper/hooks/useInAppWrapper.js +198 -0
- package/v2Containers/InAppWrapper/index.js +148 -0
- package/v2Containers/InAppWrapper/messages.js +49 -0
- package/v2Containers/InappAdvance/index.js +1099 -0
- package/v2Containers/InappAdvance/index.scss +10 -0
- package/v2Containers/InappAdvance/tests/index.test.js +448 -0
- package/v2Containers/Line/Container/ImageCarousel/tests/__snapshots__/content.test.js.snap +3 -0
- package/v2Containers/Line/Container/ImageCarousel/tests/__snapshots__/index.test.js.snap +2 -0
- package/v2Containers/Line/Container/Wrapper/tests/__snapshots__/index.test.js.snap +2 -0
- package/v2Containers/Line/Container/tests/__snapshots__/index.test.js.snap +9 -0
- package/v2Containers/MobilePush/Create/index.js +1 -1
- package/v2Containers/MobilePush/Edit/index.js +10 -6
- package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +12 -0
- package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +4 -0
- package/v2Containers/TagList/index.js +62 -19
- package/v2Containers/Templates/_templates.scss +60 -1
- package/v2Containers/Templates/index.js +89 -4
- package/v2Containers/Templates/messages.js +4 -0
- package/v2Containers/TemplatesV2/TemplatesV2.style.js +4 -2
- package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +34 -0
- package/v2Components/HtmlEditor/components/ValidationErrorDisplay/__tests__/index.test.js +0 -152
- package/v2Containers/EmailWrapper/tests/EmailWrapperView.test.js +0 -214
|
@@ -0,0 +1,473 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useInAppWrapper Hook Tests
|
|
3
|
+
*
|
|
4
|
+
* Tests for the useInAppWrapper custom hook that manages InAppWrapper component business logic
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import React from 'react';
|
|
8
|
+
import { render, screen, fireEvent, act } from '@testing-library/react';
|
|
9
|
+
import '@testing-library/jest-dom';
|
|
10
|
+
import useInAppWrapper from '../useInAppWrapper';
|
|
11
|
+
import { STEPS, INAPP_EDITOR_TYPES } from '../../constants';
|
|
12
|
+
|
|
13
|
+
// Test wrapper component
|
|
14
|
+
const TestComponent = ({ hookProps, onStateChange }) => {
|
|
15
|
+
const hookState = useInAppWrapper(hookProps);
|
|
16
|
+
|
|
17
|
+
React.useEffect(() => {
|
|
18
|
+
if (onStateChange) {
|
|
19
|
+
onStateChange(hookState);
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
return (
|
|
24
|
+
<div>
|
|
25
|
+
<div data-testid="template-name">{hookState.templateName}</div>
|
|
26
|
+
<div data-testid="is-template-name-empty">{String(hookState.isTemplateNameEmpty)}</div>
|
|
27
|
+
<div data-testid="selected-editor-type">{hookState.selectedEditorType}</div>
|
|
28
|
+
<div data-testid="is-show-inapp-create">{String(hookState.isShowInAppCreate)}</div>
|
|
29
|
+
<div data-testid="modes-count">{hookState.modes?.length || 0}</div>
|
|
30
|
+
|
|
31
|
+
<input
|
|
32
|
+
data-testid="template-name-input"
|
|
33
|
+
onChange={hookState.onTemplateNameChange}
|
|
34
|
+
value={hookState.templateName}
|
|
35
|
+
/>
|
|
36
|
+
<button
|
|
37
|
+
data-testid="editor-type-button"
|
|
38
|
+
onClick={(e) => {
|
|
39
|
+
e.target.value = INAPP_EDITOR_TYPES.HTML_EDITOR;
|
|
40
|
+
hookState.onChange(e);
|
|
41
|
+
}}
|
|
42
|
+
>
|
|
43
|
+
Change Editor Type
|
|
44
|
+
</button>
|
|
45
|
+
<button
|
|
46
|
+
data-testid="editor-type-selection-button"
|
|
47
|
+
onClick={hookState.handleEditorTypeSelection}
|
|
48
|
+
>
|
|
49
|
+
Select Editor Type
|
|
50
|
+
</button>
|
|
51
|
+
</div>
|
|
52
|
+
);
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
describe('useInAppWrapper', () => {
|
|
56
|
+
const defaultHookProps = {
|
|
57
|
+
intl: {
|
|
58
|
+
formatMessage: jest.fn((msg) => msg.defaultMessage || msg.id || 'formatted'),
|
|
59
|
+
},
|
|
60
|
+
onInAppEditorTypeChange: jest.fn(),
|
|
61
|
+
inAppEditorType: '',
|
|
62
|
+
step: STEPS.MODE_SELECTION,
|
|
63
|
+
showNextStep: jest.fn(),
|
|
64
|
+
onResetStep: jest.fn(),
|
|
65
|
+
onEnterTemplateName: jest.fn(),
|
|
66
|
+
onRemoveTemplateName: jest.fn(),
|
|
67
|
+
setIsLoadingContent: jest.fn(),
|
|
68
|
+
isGetFormData: false,
|
|
69
|
+
getFormdata: jest.fn(),
|
|
70
|
+
type: null,
|
|
71
|
+
isFullMode: true,
|
|
72
|
+
cap: {},
|
|
73
|
+
showTemplateName: jest.fn(),
|
|
74
|
+
showLiquidErrorInFooter: jest.fn(),
|
|
75
|
+
onValidationFail: jest.fn(),
|
|
76
|
+
forwardedTags: [],
|
|
77
|
+
selectedOfferDetails: null,
|
|
78
|
+
onPreviewContentClicked: jest.fn(),
|
|
79
|
+
onTestContentClicked: jest.fn(),
|
|
80
|
+
eventContextTags: {},
|
|
81
|
+
onCreateComplete: jest.fn(),
|
|
82
|
+
handleClose: jest.fn(),
|
|
83
|
+
templateData: null,
|
|
84
|
+
getDefaultTags: null,
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
beforeEach(() => {
|
|
88
|
+
jest.clearAllMocks();
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
describe('Initial State', () => {
|
|
92
|
+
it('initializes with default state', () => {
|
|
93
|
+
render(<TestComponent hookProps={defaultHookProps} />);
|
|
94
|
+
|
|
95
|
+
expect(screen.getByTestId('template-name')).toHaveTextContent('');
|
|
96
|
+
expect(screen.getByTestId('is-template-name-empty')).toHaveTextContent('true');
|
|
97
|
+
expect(screen.getByTestId('selected-editor-type')).toHaveTextContent('');
|
|
98
|
+
expect(screen.getByTestId('is-show-inapp-create')).toHaveTextContent('false');
|
|
99
|
+
expect(screen.getByTestId('modes-count')).toHaveTextContent('2');
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('initializes modes with correct structure', () => {
|
|
103
|
+
let capturedState = null;
|
|
104
|
+
render(
|
|
105
|
+
<TestComponent
|
|
106
|
+
hookProps={defaultHookProps}
|
|
107
|
+
onStateChange={(state) => {
|
|
108
|
+
capturedState = state;
|
|
109
|
+
}}
|
|
110
|
+
/>
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
expect(capturedState.modes).toHaveLength(2);
|
|
114
|
+
expect(capturedState.modes[0].value).toBe(INAPP_EDITOR_TYPES.HTML_EDITOR);
|
|
115
|
+
expect(capturedState.modes[1].value).toBe(INAPP_EDITOR_TYPES.DRAG_DROP_EDITOR);
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
describe('Template Name Management', () => {
|
|
120
|
+
it('updates template name when input changes', () => {
|
|
121
|
+
render(<TestComponent hookProps={defaultHookProps} />);
|
|
122
|
+
|
|
123
|
+
const input = screen.getByTestId('template-name-input');
|
|
124
|
+
fireEvent.change(input, { target: { value: 'Test Template' } });
|
|
125
|
+
|
|
126
|
+
expect(screen.getByTestId('template-name')).toHaveTextContent('Test Template');
|
|
127
|
+
expect(screen.getByTestId('is-template-name-empty')).toHaveTextContent('false');
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it('calls onEnterTemplateName when template name is entered', () => {
|
|
131
|
+
const onEnterTemplateName = jest.fn();
|
|
132
|
+
render(
|
|
133
|
+
<TestComponent
|
|
134
|
+
hookProps={{
|
|
135
|
+
...defaultHookProps,
|
|
136
|
+
onEnterTemplateName,
|
|
137
|
+
}}
|
|
138
|
+
/>
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
const input = screen.getByTestId('template-name-input');
|
|
142
|
+
fireEvent.change(input, { target: { value: 'Test Template' } });
|
|
143
|
+
|
|
144
|
+
expect(onEnterTemplateName).toHaveBeenCalled();
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it('calls onRemoveTemplateName when template name is cleared', () => {
|
|
148
|
+
const onRemoveTemplateName = jest.fn();
|
|
149
|
+
render(
|
|
150
|
+
<TestComponent
|
|
151
|
+
hookProps={{
|
|
152
|
+
...defaultHookProps,
|
|
153
|
+
onRemoveTemplateName,
|
|
154
|
+
}}
|
|
155
|
+
/>
|
|
156
|
+
);
|
|
157
|
+
|
|
158
|
+
const input = screen.getByTestId('template-name-input');
|
|
159
|
+
fireEvent.change(input, { target: { value: 'Test' } });
|
|
160
|
+
fireEvent.change(input, { target: { value: '' } });
|
|
161
|
+
|
|
162
|
+
expect(onRemoveTemplateName).toHaveBeenCalled();
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it('handles template name with only whitespace', () => {
|
|
166
|
+
render(<TestComponent hookProps={defaultHookProps} />);
|
|
167
|
+
|
|
168
|
+
const input = screen.getByTestId('template-name-input');
|
|
169
|
+
fireEvent.change(input, { target: { value: ' ' } });
|
|
170
|
+
|
|
171
|
+
expect(screen.getByTestId('is-template-name-empty')).toHaveTextContent('true');
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
describe('Editor Type Selection', () => {
|
|
176
|
+
it('calls onInAppEditorTypeChange when editor type changes', () => {
|
|
177
|
+
const onInAppEditorTypeChange = jest.fn();
|
|
178
|
+
render(
|
|
179
|
+
<TestComponent
|
|
180
|
+
hookProps={{
|
|
181
|
+
...defaultHookProps,
|
|
182
|
+
onInAppEditorTypeChange,
|
|
183
|
+
}}
|
|
184
|
+
/>
|
|
185
|
+
);
|
|
186
|
+
|
|
187
|
+
const button = screen.getByTestId('editor-type-button');
|
|
188
|
+
fireEvent.click(button);
|
|
189
|
+
|
|
190
|
+
expect(onInAppEditorTypeChange).toHaveBeenCalled();
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
it('handles editor type selection when template name is not empty', () => {
|
|
194
|
+
const showNextStep = jest.fn();
|
|
195
|
+
render(
|
|
196
|
+
<TestComponent
|
|
197
|
+
hookProps={{
|
|
198
|
+
...defaultHookProps,
|
|
199
|
+
inAppEditorType: INAPP_EDITOR_TYPES.HTML_EDITOR,
|
|
200
|
+
showNextStep,
|
|
201
|
+
}}
|
|
202
|
+
/>
|
|
203
|
+
);
|
|
204
|
+
|
|
205
|
+
// First set template name
|
|
206
|
+
const input = screen.getByTestId('template-name-input');
|
|
207
|
+
fireEvent.change(input, { target: { value: 'Test Template' } });
|
|
208
|
+
|
|
209
|
+
// Then select editor type
|
|
210
|
+
const button = screen.getByTestId('editor-type-selection-button');
|
|
211
|
+
fireEvent.click(button);
|
|
212
|
+
|
|
213
|
+
expect(showNextStep).toHaveBeenCalled();
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
it('does not call showNextStep when template name is empty', () => {
|
|
217
|
+
const showNextStep = jest.fn();
|
|
218
|
+
render(
|
|
219
|
+
<TestComponent
|
|
220
|
+
hookProps={{
|
|
221
|
+
...defaultHookProps,
|
|
222
|
+
inAppEditorType: INAPP_EDITOR_TYPES.HTML_EDITOR,
|
|
223
|
+
showNextStep,
|
|
224
|
+
}}
|
|
225
|
+
/>
|
|
226
|
+
);
|
|
227
|
+
|
|
228
|
+
const button = screen.getByTestId('editor-type-selection-button');
|
|
229
|
+
fireEvent.click(button);
|
|
230
|
+
|
|
231
|
+
expect(showNextStep).not.toHaveBeenCalled();
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
it('does not call showNextStep when editor type is not selected', () => {
|
|
235
|
+
const showNextStep = jest.fn();
|
|
236
|
+
render(
|
|
237
|
+
<TestComponent
|
|
238
|
+
hookProps={{
|
|
239
|
+
...defaultHookProps,
|
|
240
|
+
inAppEditorType: '',
|
|
241
|
+
showNextStep,
|
|
242
|
+
}}
|
|
243
|
+
/>
|
|
244
|
+
);
|
|
245
|
+
|
|
246
|
+
const input = screen.getByTestId('template-name-input');
|
|
247
|
+
fireEvent.change(input, { target: { value: 'Test Template' } });
|
|
248
|
+
|
|
249
|
+
const button = screen.getByTestId('editor-type-selection-button');
|
|
250
|
+
fireEvent.click(button);
|
|
251
|
+
|
|
252
|
+
expect(showNextStep).not.toHaveBeenCalled();
|
|
253
|
+
});
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
describe('Step Management', () => {
|
|
257
|
+
it('sets selectedEditorType when step is CREATE_TEMPLATE_CONTENT and editor type is set', () => {
|
|
258
|
+
let capturedState = null;
|
|
259
|
+
render(
|
|
260
|
+
<TestComponent
|
|
261
|
+
hookProps={{
|
|
262
|
+
...defaultHookProps,
|
|
263
|
+
step: STEPS.CREATE_TEMPLATE_CONTENT,
|
|
264
|
+
inAppEditorType: INAPP_EDITOR_TYPES.HTML_EDITOR,
|
|
265
|
+
}}
|
|
266
|
+
onStateChange={(state) => {
|
|
267
|
+
capturedState = state;
|
|
268
|
+
}}
|
|
269
|
+
/>
|
|
270
|
+
);
|
|
271
|
+
|
|
272
|
+
expect(capturedState.selectedEditorType).toBe(INAPP_EDITOR_TYPES.HTML_EDITOR);
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
it('does not set selectedEditorType when step is MODE_SELECTION', () => {
|
|
276
|
+
let capturedState = null;
|
|
277
|
+
render(
|
|
278
|
+
<TestComponent
|
|
279
|
+
hookProps={{
|
|
280
|
+
...defaultHookProps,
|
|
281
|
+
step: STEPS.MODE_SELECTION,
|
|
282
|
+
inAppEditorType: INAPP_EDITOR_TYPES.HTML_EDITOR,
|
|
283
|
+
}}
|
|
284
|
+
onStateChange={(state) => {
|
|
285
|
+
capturedState = state;
|
|
286
|
+
}}
|
|
287
|
+
/>
|
|
288
|
+
);
|
|
289
|
+
|
|
290
|
+
expect(capturedState.selectedEditorType).toBe('');
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
it('skips setting selectedEditorType if already set', () => {
|
|
294
|
+
let capturedState = null;
|
|
295
|
+
const { rerender } = render(
|
|
296
|
+
<TestComponent
|
|
297
|
+
hookProps={{
|
|
298
|
+
...defaultHookProps,
|
|
299
|
+
step: STEPS.CREATE_TEMPLATE_CONTENT,
|
|
300
|
+
inAppEditorType: INAPP_EDITOR_TYPES.HTML_EDITOR,
|
|
301
|
+
}}
|
|
302
|
+
onStateChange={(state) => {
|
|
303
|
+
capturedState = state;
|
|
304
|
+
}}
|
|
305
|
+
/>
|
|
306
|
+
);
|
|
307
|
+
|
|
308
|
+
const initialType = capturedState.selectedEditorType;
|
|
309
|
+
|
|
310
|
+
// Change step but keep editor type
|
|
311
|
+
rerender(
|
|
312
|
+
<TestComponent
|
|
313
|
+
hookProps={{
|
|
314
|
+
...defaultHookProps,
|
|
315
|
+
step: STEPS.MODE_SELECTION,
|
|
316
|
+
inAppEditorType: INAPP_EDITOR_TYPES.HTML_EDITOR,
|
|
317
|
+
}}
|
|
318
|
+
onStateChange={(state) => {
|
|
319
|
+
capturedState = state;
|
|
320
|
+
}}
|
|
321
|
+
/>
|
|
322
|
+
);
|
|
323
|
+
|
|
324
|
+
// Should remain the same since it was already set
|
|
325
|
+
expect(capturedState.selectedEditorType).toBe(initialType);
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
it('handles default case in step switch statement', () => {
|
|
329
|
+
let capturedState = null;
|
|
330
|
+
render(
|
|
331
|
+
<TestComponent
|
|
332
|
+
hookProps={{
|
|
333
|
+
...defaultHookProps,
|
|
334
|
+
step: 'UNKNOWN_STEP', // Use an unknown step to trigger default case
|
|
335
|
+
inAppEditorType: INAPP_EDITOR_TYPES.HTML_EDITOR,
|
|
336
|
+
}}
|
|
337
|
+
onStateChange={(state) => {
|
|
338
|
+
capturedState = state;
|
|
339
|
+
}}
|
|
340
|
+
/>
|
|
341
|
+
);
|
|
342
|
+
|
|
343
|
+
// Should not set selectedEditorType for unknown step
|
|
344
|
+
expect(capturedState.selectedEditorType).toBe('');
|
|
345
|
+
});
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
describe('Cleanup', () => {
|
|
349
|
+
it('calls onResetStep on unmount', () => {
|
|
350
|
+
const onResetStep = jest.fn();
|
|
351
|
+
const { unmount } = render(
|
|
352
|
+
<TestComponent
|
|
353
|
+
hookProps={{
|
|
354
|
+
...defaultHookProps,
|
|
355
|
+
onResetStep,
|
|
356
|
+
}}
|
|
357
|
+
/>
|
|
358
|
+
);
|
|
359
|
+
|
|
360
|
+
unmount();
|
|
361
|
+
|
|
362
|
+
expect(onResetStep).toHaveBeenCalled();
|
|
363
|
+
});
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
describe('InApp Props Generation', () => {
|
|
367
|
+
it('generates correct inAppProps with all required fields', () => {
|
|
368
|
+
let capturedState = null;
|
|
369
|
+
render(
|
|
370
|
+
<TestComponent
|
|
371
|
+
hookProps={{
|
|
372
|
+
...defaultHookProps,
|
|
373
|
+
templateName: 'Test Template',
|
|
374
|
+
inAppEditorType: INAPP_EDITOR_TYPES.HTML_EDITOR,
|
|
375
|
+
}}
|
|
376
|
+
onStateChange={(state) => {
|
|
377
|
+
capturedState = state;
|
|
378
|
+
}}
|
|
379
|
+
/>
|
|
380
|
+
);
|
|
381
|
+
|
|
382
|
+
const input = screen.getByTestId('template-name-input');
|
|
383
|
+
fireEvent.change(input, { target: { value: 'Test Template' } });
|
|
384
|
+
|
|
385
|
+
expect(capturedState.inAppProps).toBeDefined();
|
|
386
|
+
expect(capturedState.inAppProps.defaultData['template-name']).toBe('Test Template');
|
|
387
|
+
expect(capturedState.inAppProps.defaultData['editor-type']).toBe(INAPP_EDITOR_TYPES.HTML_EDITOR);
|
|
388
|
+
expect(capturedState.inAppProps.key).toBe('inapp-create-template');
|
|
389
|
+
expect(capturedState.inAppProps.route.name).toBe('inapp');
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
it('uses getDefaultTags from type prop when available', () => {
|
|
393
|
+
let capturedState = null;
|
|
394
|
+
render(
|
|
395
|
+
<TestComponent
|
|
396
|
+
hookProps={{
|
|
397
|
+
...defaultHookProps,
|
|
398
|
+
type: 'customType',
|
|
399
|
+
getDefaultTags: 'defaultTags',
|
|
400
|
+
}}
|
|
401
|
+
onStateChange={(state) => {
|
|
402
|
+
capturedState = state;
|
|
403
|
+
}}
|
|
404
|
+
/>
|
|
405
|
+
);
|
|
406
|
+
|
|
407
|
+
expect(capturedState.inAppProps.getDefaultTags).toBe('customType');
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
it('uses getDefaultTags prop when type is not available', () => {
|
|
411
|
+
let capturedState = null;
|
|
412
|
+
render(
|
|
413
|
+
<TestComponent
|
|
414
|
+
hookProps={{
|
|
415
|
+
...defaultHookProps,
|
|
416
|
+
type: null,
|
|
417
|
+
getDefaultTags: 'defaultTags',
|
|
418
|
+
}}
|
|
419
|
+
onStateChange={(state) => {
|
|
420
|
+
capturedState = state;
|
|
421
|
+
}}
|
|
422
|
+
/>
|
|
423
|
+
);
|
|
424
|
+
|
|
425
|
+
expect(capturedState.inAppProps.getDefaultTags).toBe('defaultTags');
|
|
426
|
+
});
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
describe('isShowInAppCreate', () => {
|
|
430
|
+
it('returns truthy value when selectedEditorType and inAppEditorType are set', () => {
|
|
431
|
+
let capturedState = null;
|
|
432
|
+
render(
|
|
433
|
+
<TestComponent
|
|
434
|
+
hookProps={{
|
|
435
|
+
...defaultHookProps,
|
|
436
|
+
step: STEPS.CREATE_TEMPLATE_CONTENT,
|
|
437
|
+
inAppEditorType: INAPP_EDITOR_TYPES.HTML_EDITOR,
|
|
438
|
+
}}
|
|
439
|
+
onStateChange={(state) => {
|
|
440
|
+
capturedState = state;
|
|
441
|
+
}}
|
|
442
|
+
/>
|
|
443
|
+
);
|
|
444
|
+
|
|
445
|
+
// isShowInAppCreate = !isEmpty(selectedEditorType) && inAppEditorType
|
|
446
|
+
// When both are truthy, it returns inAppEditorType (the string value)
|
|
447
|
+
// After useEffect runs, selectedEditorType should be set
|
|
448
|
+
expect(capturedState.selectedEditorType).toBe(INAPP_EDITOR_TYPES.HTML_EDITOR);
|
|
449
|
+
// isShowInAppCreate returns the inAppEditorType value when both conditions are true
|
|
450
|
+
expect(capturedState.isShowInAppCreate).toBeTruthy();
|
|
451
|
+
expect(capturedState.isShowInAppCreate).toBe(INAPP_EDITOR_TYPES.HTML_EDITOR);
|
|
452
|
+
});
|
|
453
|
+
|
|
454
|
+
it('returns false when selectedEditorType is not set', () => {
|
|
455
|
+
let capturedState = null;
|
|
456
|
+
render(
|
|
457
|
+
<TestComponent
|
|
458
|
+
hookProps={{
|
|
459
|
+
...defaultHookProps,
|
|
460
|
+
step: STEPS.MODE_SELECTION,
|
|
461
|
+
inAppEditorType: INAPP_EDITOR_TYPES.HTML_EDITOR,
|
|
462
|
+
}}
|
|
463
|
+
onStateChange={(state) => {
|
|
464
|
+
capturedState = state;
|
|
465
|
+
}}
|
|
466
|
+
/>
|
|
467
|
+
);
|
|
468
|
+
|
|
469
|
+
expect(capturedState.isShowInAppCreate).toBe(false);
|
|
470
|
+
});
|
|
471
|
+
});
|
|
472
|
+
});
|
|
473
|
+
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import React, {
|
|
2
|
+
useState, useEffect, useMemo, useCallback,
|
|
3
|
+
} from 'react';
|
|
4
|
+
import isEmpty from 'lodash/isEmpty';
|
|
5
|
+
import CapIcon from '@capillarytech/cap-ui-library/CapIcon';
|
|
6
|
+
import messages from '../messages';
|
|
7
|
+
import { STEPS, INAPP_EDITOR_TYPES } from '../constants';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Custom hook to handle InAppWrapper component business logic
|
|
11
|
+
*/
|
|
12
|
+
const useInAppWrapper = ({
|
|
13
|
+
intl: { formatMessage },
|
|
14
|
+
onInAppEditorTypeChange,
|
|
15
|
+
inAppEditorType,
|
|
16
|
+
step,
|
|
17
|
+
showNextStep,
|
|
18
|
+
onResetStep,
|
|
19
|
+
onEnterTemplateName,
|
|
20
|
+
onRemoveTemplateName,
|
|
21
|
+
// Props for InApp component
|
|
22
|
+
setIsLoadingContent,
|
|
23
|
+
isGetFormData,
|
|
24
|
+
getFormdata,
|
|
25
|
+
type,
|
|
26
|
+
isFullMode,
|
|
27
|
+
cap,
|
|
28
|
+
showTemplateName,
|
|
29
|
+
showLiquidErrorInFooter,
|
|
30
|
+
onValidationFail,
|
|
31
|
+
forwardedTags,
|
|
32
|
+
selectedOfferDetails,
|
|
33
|
+
onPreviewContentClicked,
|
|
34
|
+
onTestContentClicked,
|
|
35
|
+
eventContextTags,
|
|
36
|
+
onCreateComplete,
|
|
37
|
+
handleClose,
|
|
38
|
+
templateData,
|
|
39
|
+
getDefaultTags,
|
|
40
|
+
}) => {
|
|
41
|
+
// State management
|
|
42
|
+
const [templateName, setTemplateName] = useState('');
|
|
43
|
+
const [isTemplateNameEmpty, setIsTemplateNameEmpty] = useState(true);
|
|
44
|
+
const [selectedEditorType, setSelectedEditorType] = useState('');
|
|
45
|
+
const [routeParams] = useState({
|
|
46
|
+
pathname: `/inapp/create`,
|
|
47
|
+
query: { module: 'library', type: 'embedded' },
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
// Cleanup effect
|
|
51
|
+
useEffect(() => () => {
|
|
52
|
+
onResetStep();
|
|
53
|
+
}, [onResetStep]);
|
|
54
|
+
|
|
55
|
+
// Event handlers
|
|
56
|
+
const onTemplateNameChange = useCallback(({target: {value}}) => {
|
|
57
|
+
const isEmptyTemplateName = !value?.trim();
|
|
58
|
+
setTemplateName(value);
|
|
59
|
+
setIsTemplateNameEmpty(isEmptyTemplateName);
|
|
60
|
+
|
|
61
|
+
if (value && onEnterTemplateName) {
|
|
62
|
+
onEnterTemplateName();
|
|
63
|
+
} else if (onRemoveTemplateName) {
|
|
64
|
+
onRemoveTemplateName();
|
|
65
|
+
}
|
|
66
|
+
}, [onEnterTemplateName, onRemoveTemplateName]);
|
|
67
|
+
|
|
68
|
+
const onChange = useCallback((e) => {
|
|
69
|
+
onInAppEditorTypeChange(e.target.value);
|
|
70
|
+
}, [onInAppEditorTypeChange]);
|
|
71
|
+
|
|
72
|
+
const handleEditorTypeSelection = useCallback(() => {
|
|
73
|
+
if (inAppEditorType && !isTemplateNameEmpty) {
|
|
74
|
+
setSelectedEditorType(inAppEditorType);
|
|
75
|
+
showNextStep();
|
|
76
|
+
}
|
|
77
|
+
}, [inAppEditorType, isTemplateNameEmpty, showNextStep]);
|
|
78
|
+
|
|
79
|
+
// Main logic effect
|
|
80
|
+
useEffect(() => {
|
|
81
|
+
// Skip if user has already made a selection
|
|
82
|
+
if (selectedEditorType) return;
|
|
83
|
+
|
|
84
|
+
// Handle different steps
|
|
85
|
+
switch (step) {
|
|
86
|
+
case STEPS.MODE_SELECTION:
|
|
87
|
+
// Wait for user to select editor type and enter template name
|
|
88
|
+
break;
|
|
89
|
+
|
|
90
|
+
case STEPS.CREATE_TEMPLATE_CONTENT:
|
|
91
|
+
if (inAppEditorType) {
|
|
92
|
+
setSelectedEditorType(inAppEditorType);
|
|
93
|
+
}
|
|
94
|
+
break;
|
|
95
|
+
|
|
96
|
+
default:
|
|
97
|
+
// No operation for other steps
|
|
98
|
+
break;
|
|
99
|
+
}
|
|
100
|
+
}, [
|
|
101
|
+
step,
|
|
102
|
+
selectedEditorType,
|
|
103
|
+
inAppEditorType,
|
|
104
|
+
]);
|
|
105
|
+
|
|
106
|
+
// Derived state
|
|
107
|
+
const isShowInAppCreate = !isEmpty(selectedEditorType) && inAppEditorType;
|
|
108
|
+
|
|
109
|
+
// Memoize static data
|
|
110
|
+
const modes = useMemo(() => [
|
|
111
|
+
{
|
|
112
|
+
title: formatMessage(messages.htmlEditor),
|
|
113
|
+
content: formatMessage(messages.htmlEditorDesc),
|
|
114
|
+
value: INAPP_EDITOR_TYPES.HTML_EDITOR,
|
|
115
|
+
icon: <CapIcon type="code" />,
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
title: formatMessage(messages.dragDropEditor),
|
|
119
|
+
content: formatMessage(messages.dragDropEditorDesc),
|
|
120
|
+
value: INAPP_EDITOR_TYPES.DRAG_DROP_EDITOR,
|
|
121
|
+
icon: <CapIcon type="draggable" />,
|
|
122
|
+
},
|
|
123
|
+
], [formatMessage]);
|
|
124
|
+
|
|
125
|
+
// Prepare props for InApp component
|
|
126
|
+
const inAppProps = useMemo(() => {
|
|
127
|
+
const props = {
|
|
128
|
+
setIsLoadingContent,
|
|
129
|
+
key: "inapp-create-template",
|
|
130
|
+
location: routeParams,
|
|
131
|
+
route: { name: 'inapp' },
|
|
132
|
+
params: {},
|
|
133
|
+
isGetFormData,
|
|
134
|
+
getFormData: getFormdata, // Map getFormdata to getFormData (matching InApp component prop name)
|
|
135
|
+
getFormSubscriptionData: getFormdata,
|
|
136
|
+
getDefaultTags: type || getDefaultTags,
|
|
137
|
+
isFullMode,
|
|
138
|
+
defaultData: {
|
|
139
|
+
'template-name': templateName,
|
|
140
|
+
'editor-type': inAppEditorType, // Pass editor type to InApp component
|
|
141
|
+
},
|
|
142
|
+
templateData,
|
|
143
|
+
cap,
|
|
144
|
+
showTemplateName,
|
|
145
|
+
showLiquidErrorInFooter,
|
|
146
|
+
onValidationFail,
|
|
147
|
+
forwardedTags,
|
|
148
|
+
selectedOfferDetails,
|
|
149
|
+
onPreviewContentClicked,
|
|
150
|
+
onTestContentClicked,
|
|
151
|
+
eventContextTags,
|
|
152
|
+
onCreateComplete,
|
|
153
|
+
handleClose,
|
|
154
|
+
};
|
|
155
|
+
return props;
|
|
156
|
+
}, [
|
|
157
|
+
setIsLoadingContent,
|
|
158
|
+
routeParams,
|
|
159
|
+
isGetFormData,
|
|
160
|
+
getFormdata,
|
|
161
|
+
type,
|
|
162
|
+
getDefaultTags,
|
|
163
|
+
isFullMode,
|
|
164
|
+
templateName,
|
|
165
|
+
inAppEditorType,
|
|
166
|
+
templateData,
|
|
167
|
+
cap,
|
|
168
|
+
showTemplateName,
|
|
169
|
+
showLiquidErrorInFooter,
|
|
170
|
+
onValidationFail,
|
|
171
|
+
forwardedTags,
|
|
172
|
+
selectedOfferDetails,
|
|
173
|
+
onPreviewContentClicked,
|
|
174
|
+
onTestContentClicked,
|
|
175
|
+
eventContextTags,
|
|
176
|
+
onCreateComplete,
|
|
177
|
+
handleClose,
|
|
178
|
+
]);
|
|
179
|
+
|
|
180
|
+
return {
|
|
181
|
+
// State
|
|
182
|
+
templateName,
|
|
183
|
+
isTemplateNameEmpty,
|
|
184
|
+
selectedEditorType,
|
|
185
|
+
|
|
186
|
+
// Derived values
|
|
187
|
+
modes,
|
|
188
|
+
isShowInAppCreate,
|
|
189
|
+
inAppProps,
|
|
190
|
+
|
|
191
|
+
// Event handlers
|
|
192
|
+
onTemplateNameChange,
|
|
193
|
+
onChange,
|
|
194
|
+
handleEditorTypeSelection,
|
|
195
|
+
};
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
export default useInAppWrapper;
|