@capillarytech/creatives-library 8.0.126 → 8.0.127-alpha.1
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/containers/App/constants.js +1 -0
- package/index.html +3 -1
- package/package.json +1 -1
- package/services/api.js +4 -4
- package/tests/integration/TemplateCreation/TemplateCreation.integration.test.js +8 -3
- package/tests/integration/TemplateCreation/api-response.js +5 -0
- package/tests/integration/TemplateCreation/msw-handler.js +42 -63
- package/utils/common.js +7 -0
- package/utils/commonUtils.js +2 -6
- package/utils/createPayload.js +272 -0
- package/utils/tests/createPayload.test.js +761 -0
- package/v2Components/CapImageUpload/index.js +59 -46
- package/v2Components/CapInAppCTA/index.js +1 -0
- package/v2Components/CapMpushCTA/constants.js +25 -0
- package/v2Components/CapMpushCTA/index.js +332 -0
- package/v2Components/CapMpushCTA/index.scss +95 -0
- package/v2Components/CapMpushCTA/messages.js +89 -0
- package/v2Components/CapTagList/index.js +177 -120
- package/v2Components/CapVideoUpload/constants.js +3 -0
- package/v2Components/CapVideoUpload/index.js +167 -110
- package/v2Components/CapVideoUpload/messages.js +16 -0
- package/v2Components/Carousel/index.js +15 -13
- package/v2Components/CustomerSearchSection/index.js +12 -7
- package/v2Components/ErrorInfoNote/style.scss +1 -0
- package/v2Components/MobilePushPreviewV2/index.js +37 -5
- package/v2Components/TemplatePreview/_templatePreview.scss +114 -72
- package/v2Components/TemplatePreview/assets/images/Android _ With date and time.svg +29 -0
- package/v2Components/TemplatePreview/assets/images/android.svg +9 -0
- package/v2Components/TemplatePreview/assets/images/iOS _ With date and time.svg +26 -0
- package/v2Components/TemplatePreview/assets/images/ios.svg +9 -0
- package/v2Components/TemplatePreview/index.js +178 -50
- package/v2Components/TemplatePreview/messages.js +4 -0
- package/v2Components/TestAndPreviewSlidebox/CustomValuesEditor.js +169 -0
- package/v2Components/TestAndPreviewSlidebox/LeftPanelContent.js +95 -0
- package/v2Components/TestAndPreviewSlidebox/PreviewSection.js +69 -0
- package/v2Components/TestAndPreviewSlidebox/SendTestMessage.js +68 -0
- package/v2Components/TestAndPreviewSlidebox/index.js +67 -246
- package/v2Components/TestAndPreviewSlidebox/tests/CustomValuesEditor.test.js +425 -0
- package/v2Components/TestAndPreviewSlidebox/tests/LeftPanelContent.test.js +400 -0
- package/v2Components/TestAndPreviewSlidebox/tests/SendTestMessage.test.js +448 -0
- package/v2Containers/CreativesContainer/SlideBoxContent.js +9 -9
- package/v2Containers/CreativesContainer/index.js +191 -136
- package/v2Containers/Email/index.js +15 -2
- package/v2Containers/InApp/constants.js +1 -0
- package/v2Containers/InApp/index.js +13 -13
- package/v2Containers/MobilePush/Create/index.js +1 -0
- package/v2Containers/MobilePush/commonMethods.js +7 -14
- package/v2Containers/MobilePushNew/actions.js +116 -0
- package/v2Containers/MobilePushNew/components/CtaButtons.js +170 -0
- package/v2Containers/MobilePushNew/components/MediaUploaders.js +754 -0
- package/v2Containers/MobilePushNew/components/PlatformContentFields.js +279 -0
- package/v2Containers/MobilePushNew/components/index.js +5 -0
- package/v2Containers/MobilePushNew/components/tests/CtaButtons.test.js +779 -0
- package/v2Containers/MobilePushNew/components/tests/MediaUploaders.test.js +2114 -0
- package/v2Containers/MobilePushNew/components/tests/PlatformContentFields.test.js +343 -0
- package/v2Containers/MobilePushNew/constants.js +115 -0
- package/v2Containers/MobilePushNew/hooks/tests/usePlatformSync.test.js +1299 -0
- package/v2Containers/MobilePushNew/hooks/tests/useUpload.test.js +1223 -0
- package/v2Containers/MobilePushNew/hooks/usePlatformSync.js +246 -0
- package/v2Containers/MobilePushNew/hooks/useUpload.js +726 -0
- package/v2Containers/MobilePushNew/index.js +2280 -0
- package/v2Containers/MobilePushNew/index.scss +308 -0
- package/v2Containers/MobilePushNew/messages.js +226 -0
- package/v2Containers/MobilePushNew/reducer.js +160 -0
- package/v2Containers/MobilePushNew/sagas.js +198 -0
- package/v2Containers/MobilePushNew/selectors.js +55 -0
- package/v2Containers/MobilePushNew/tests/reducer.test.js +741 -0
- package/v2Containers/MobilePushNew/tests/sagas.test.js +863 -0
- package/v2Containers/MobilePushNew/tests/selectors.test.js +425 -0
- package/v2Containers/MobilePushNew/tests/utils.test.js +322 -0
- package/v2Containers/MobilePushNew/utils.js +33 -0
- package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +5 -5
- package/v2Containers/TagList/index.js +56 -10
- package/v2Containers/Templates/_templates.scss +101 -1
- package/v2Containers/Templates/index.js +147 -35
- package/v2Containers/Templates/messages.js +8 -0
- package/v2Containers/Templates/sagas.js +2 -0
- package/v2Containers/Whatsapp/constants.js +1 -0
|
@@ -0,0 +1,779 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render, screen, fireEvent, act } from '@testing-library/react';
|
|
3
|
+
import '@testing-library/jest-dom';
|
|
4
|
+
import { Provider } from 'react-redux';
|
|
5
|
+
import { IntlProvider } from 'react-intl';
|
|
6
|
+
import { configureStore } from '@capillarytech/vulcan-react-sdk/utils';
|
|
7
|
+
import { initialReducer } from '../../../../initialReducer';
|
|
8
|
+
import history from '../../../../utils/history';
|
|
9
|
+
import CtaButtons from '../CtaButtons';
|
|
10
|
+
import { PRIMARY, SECONDARY, DEEP_LINK } from '../../constants';
|
|
11
|
+
|
|
12
|
+
// Mock the UI components
|
|
13
|
+
jest.mock('@capillarytech/cap-ui-library/CapRow', () =>
|
|
14
|
+
function MockCapRow({ children }) {
|
|
15
|
+
return <div data-testid="cap-row">{children}</div>;
|
|
16
|
+
}
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
jest.mock('@capillarytech/cap-ui-library/CapButton', () =>
|
|
20
|
+
function MockCapButton({ children, onClick, id, type, ...props }) {
|
|
21
|
+
return (
|
|
22
|
+
<button
|
|
23
|
+
data-testid={id || 'cap-button'}
|
|
24
|
+
onClick={onClick}
|
|
25
|
+
type={type || 'button'}
|
|
26
|
+
{...props}
|
|
27
|
+
>
|
|
28
|
+
{children}
|
|
29
|
+
</button>
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
jest.mock('@capillarytech/cap-ui-library/CapIcon', () =>
|
|
35
|
+
function MockCapIcon({ type }) {
|
|
36
|
+
return <span data-testid="cap-icon" data-icon-type={type} />;
|
|
37
|
+
}
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
jest.mock('@capillarytech/cap-ui-library/CapHeading', () =>
|
|
41
|
+
function MockCapHeading({ children, type, id }) {
|
|
42
|
+
return (
|
|
43
|
+
<div data-testid={id || 'cap-heading'} data-heading-type={type}>
|
|
44
|
+
{children}
|
|
45
|
+
</div>
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
// Mock the CapMpushCTA component
|
|
51
|
+
jest.mock('../../../../v2Components/CapMpushCTA', () =>
|
|
52
|
+
function MockCapMpushCTA({ ctaData, updateHandler, deleteHandler, deepLink, buttonType }) {
|
|
53
|
+
return (
|
|
54
|
+
<div data-testid="cap-mpush-cta" data-button-type={buttonType}>
|
|
55
|
+
<button
|
|
56
|
+
data-testid={`${buttonType}-cta-update`}
|
|
57
|
+
onClick={() => updateHandler({ text: 'Updated' }, buttonType === 'PRIMARY' ? 0 : 1)}
|
|
58
|
+
>
|
|
59
|
+
Update CTA
|
|
60
|
+
</button>
|
|
61
|
+
<button
|
|
62
|
+
data-testid={`${buttonType}-cta-delete`}
|
|
63
|
+
onClick={() => deleteHandler(buttonType === 'PRIMARY' ? 0 : 1)}
|
|
64
|
+
>
|
|
65
|
+
Delete CTA
|
|
66
|
+
</button>
|
|
67
|
+
</div>
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
// Mock the constants
|
|
73
|
+
jest.mock('../../constants', () => ({
|
|
74
|
+
PRIMARY: 'PRIMARY',
|
|
75
|
+
SECONDARY: 'SECONDARY',
|
|
76
|
+
DEEP_LINK: 'DEEP_LINK',
|
|
77
|
+
}));
|
|
78
|
+
|
|
79
|
+
// Mock the messages
|
|
80
|
+
jest.mock('../../messages', () => ({
|
|
81
|
+
buttons: {
|
|
82
|
+
id: 'buttons',
|
|
83
|
+
defaultMessage: 'Buttons',
|
|
84
|
+
},
|
|
85
|
+
addPrimaryButton: {
|
|
86
|
+
id: 'add.primary.button',
|
|
87
|
+
defaultMessage: 'Add primary button',
|
|
88
|
+
},
|
|
89
|
+
addSecondaryButton: {
|
|
90
|
+
id: 'add.secondary.button',
|
|
91
|
+
defaultMessage: 'Add secondary button',
|
|
92
|
+
},
|
|
93
|
+
}));
|
|
94
|
+
|
|
95
|
+
const mockStore = configureStore({}, initialReducer, history);
|
|
96
|
+
|
|
97
|
+
const defaultProps = {
|
|
98
|
+
primaryButton: false,
|
|
99
|
+
secondaryButton: false,
|
|
100
|
+
setPrimaryButton: jest.fn(),
|
|
101
|
+
setSecondaryButton: jest.fn(),
|
|
102
|
+
ctaData: [],
|
|
103
|
+
updateHandler: jest.fn(),
|
|
104
|
+
deleteHandler: jest.fn(),
|
|
105
|
+
deepLink: [],
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
const renderComponent = (props = {}) => {
|
|
109
|
+
const mergedProps = { ...defaultProps, ...props };
|
|
110
|
+
return render(
|
|
111
|
+
<Provider store={mockStore}>
|
|
112
|
+
<IntlProvider locale="en" messages={{}}>
|
|
113
|
+
<CtaButtons {...mergedProps} />
|
|
114
|
+
</IntlProvider>
|
|
115
|
+
</Provider>
|
|
116
|
+
);
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
describe('CtaButtons', () => {
|
|
120
|
+
beforeEach(() => {
|
|
121
|
+
jest.clearAllMocks();
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
describe('Initial Rendering', () => {
|
|
125
|
+
it('should render the component with default props', () => {
|
|
126
|
+
renderComponent();
|
|
127
|
+
expect(screen.getByTestId('cap-row')).toBeInTheDocument();
|
|
128
|
+
expect(screen.getByText('Buttons')).toBeInTheDocument();
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it('should render add primary button when no primary button exists', () => {
|
|
132
|
+
renderComponent();
|
|
133
|
+
expect(screen.getByTestId('add-primary-button')).toBeInTheDocument();
|
|
134
|
+
expect(screen.getByText('Add primary button')).toBeInTheDocument();
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it('should not render add primary button when primary button is saved', () => {
|
|
138
|
+
const ctaData = [
|
|
139
|
+
{ index: 0, text: 'Primary Button', isSaved: true }
|
|
140
|
+
];
|
|
141
|
+
renderComponent({ ctaData });
|
|
142
|
+
expect(screen.queryByTestId('add-primary-button')).not.toBeInTheDocument();
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it('should not render add primary button when primary CTA is being shown', () => {
|
|
146
|
+
const ctaData = [
|
|
147
|
+
{ index: 0, text: 'Primary Button', ctaType: 'PRIMARY' }
|
|
148
|
+
];
|
|
149
|
+
renderComponent({ primaryButton: true, ctaData });
|
|
150
|
+
expect(screen.queryByTestId('add-primary-button')).not.toBeInTheDocument();
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
describe('Primary Button Functionality', () => {
|
|
155
|
+
it('should handle adding primary button', () => {
|
|
156
|
+
const setPrimaryButton = jest.fn();
|
|
157
|
+
const updateHandler = jest.fn();
|
|
158
|
+
|
|
159
|
+
renderComponent({ setPrimaryButton, updateHandler });
|
|
160
|
+
|
|
161
|
+
fireEvent.click(screen.getByTestId('add-primary-button'));
|
|
162
|
+
|
|
163
|
+
expect(updateHandler).toHaveBeenCalledWith({
|
|
164
|
+
index: 0,
|
|
165
|
+
ctaType: 'PRIMARY',
|
|
166
|
+
text: "",
|
|
167
|
+
urlType: 'DEEP_LINK',
|
|
168
|
+
url: "",
|
|
169
|
+
isSaved: false,
|
|
170
|
+
}, 0);
|
|
171
|
+
expect(setPrimaryButton).toHaveBeenCalledWith(true);
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
it('should prevent multiple clicks when primary button is already being added', () => {
|
|
175
|
+
const setPrimaryButton = jest.fn();
|
|
176
|
+
const updateHandler = jest.fn();
|
|
177
|
+
const ctaData = [
|
|
178
|
+
{ index: 0, text: 'Primary Button', ctaType: 'PRIMARY' }
|
|
179
|
+
];
|
|
180
|
+
|
|
181
|
+
renderComponent({
|
|
182
|
+
primaryButton: true,
|
|
183
|
+
ctaData,
|
|
184
|
+
setPrimaryButton,
|
|
185
|
+
updateHandler
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
// Should not render add button when primaryButton is true and button exists
|
|
189
|
+
expect(screen.queryByTestId('add-primary-button')).not.toBeInTheDocument();
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
it('should not initialize primary button if it already exists in ctaData', () => {
|
|
193
|
+
const updateHandler = jest.fn();
|
|
194
|
+
const ctaData = [
|
|
195
|
+
{ index: 0, text: 'Existing Primary', ctaType: 'PRIMARY' }
|
|
196
|
+
];
|
|
197
|
+
|
|
198
|
+
renderComponent({ primaryButton: true, ctaData, updateHandler });
|
|
199
|
+
|
|
200
|
+
// Since primary button exists and primaryButton is true, add button should not be shown
|
|
201
|
+
expect(screen.queryByTestId('add-primary-button')).not.toBeInTheDocument();
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
it('should show primary CTA when primary button is active and exists', () => {
|
|
205
|
+
const ctaData = [
|
|
206
|
+
{ index: 0, text: 'Primary Button', ctaType: 'PRIMARY' }
|
|
207
|
+
];
|
|
208
|
+
|
|
209
|
+
renderComponent({
|
|
210
|
+
primaryButton: true,
|
|
211
|
+
ctaData
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
expect(screen.getByTestId('cap-mpush-cta')).toBeInTheDocument();
|
|
215
|
+
expect(screen.getByTestId('cap-mpush-cta')).toHaveAttribute('data-button-type', 'PRIMARY');
|
|
216
|
+
});
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
describe('Secondary Button Functionality', () => {
|
|
220
|
+
it('should render add secondary button when primary button is saved', () => {
|
|
221
|
+
const ctaData = [
|
|
222
|
+
{ index: 0, text: 'Primary Button', isSaved: true }
|
|
223
|
+
];
|
|
224
|
+
|
|
225
|
+
renderComponent({ ctaData });
|
|
226
|
+
|
|
227
|
+
expect(screen.getByTestId('add-secondary-button')).toBeInTheDocument();
|
|
228
|
+
expect(screen.getByText('Add secondary button')).toBeInTheDocument();
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
it('should not render add secondary button when primary button is not saved', () => {
|
|
232
|
+
const ctaData = [
|
|
233
|
+
{ index: 0, text: 'Primary Button', isSaved: false }
|
|
234
|
+
];
|
|
235
|
+
|
|
236
|
+
renderComponent({ ctaData });
|
|
237
|
+
|
|
238
|
+
expect(screen.queryByTestId('add-secondary-button')).not.toBeInTheDocument();
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
it('should handle adding secondary button', () => {
|
|
242
|
+
const setSecondaryButton = jest.fn();
|
|
243
|
+
const updateHandler = jest.fn();
|
|
244
|
+
const ctaData = [
|
|
245
|
+
{ index: 0, text: 'Primary Button', isSaved: true }
|
|
246
|
+
];
|
|
247
|
+
|
|
248
|
+
renderComponent({
|
|
249
|
+
ctaData,
|
|
250
|
+
setSecondaryButton,
|
|
251
|
+
updateHandler
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
fireEvent.click(screen.getByTestId('add-secondary-button'));
|
|
255
|
+
|
|
256
|
+
expect(updateHandler).toHaveBeenCalledWith({
|
|
257
|
+
index: 1,
|
|
258
|
+
ctaType: 'SECONDARY',
|
|
259
|
+
text: "",
|
|
260
|
+
urlType: 'DEEP_LINK',
|
|
261
|
+
url: "",
|
|
262
|
+
isSaved: false,
|
|
263
|
+
}, 1);
|
|
264
|
+
expect(setSecondaryButton).toHaveBeenCalledWith(true);
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
it('should prevent multiple clicks when secondary button is already being added', () => {
|
|
268
|
+
const setSecondaryButton = jest.fn();
|
|
269
|
+
const updateHandler = jest.fn();
|
|
270
|
+
const ctaData = [
|
|
271
|
+
{ index: 0, text: 'Primary Button', isSaved: true },
|
|
272
|
+
{ index: 1, text: 'Secondary Button', ctaType: 'SECONDARY' }
|
|
273
|
+
];
|
|
274
|
+
|
|
275
|
+
renderComponent({
|
|
276
|
+
ctaData,
|
|
277
|
+
secondaryButton: true,
|
|
278
|
+
setSecondaryButton,
|
|
279
|
+
updateHandler
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
// Should not render add button when secondaryButton is true and button exists
|
|
283
|
+
expect(screen.queryByTestId('add-secondary-button')).not.toBeInTheDocument();
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
it('should not render add secondary button when secondary button is saved', () => {
|
|
287
|
+
const ctaData = [
|
|
288
|
+
{ index: 0, text: 'Primary Button', isSaved: true },
|
|
289
|
+
{ index: 1, text: 'Secondary Button', isSaved: true }
|
|
290
|
+
];
|
|
291
|
+
|
|
292
|
+
renderComponent({ ctaData });
|
|
293
|
+
|
|
294
|
+
expect(screen.queryByTestId('add-secondary-button')).not.toBeInTheDocument();
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
it('should show secondary CTA when secondary button is active and exists', () => {
|
|
298
|
+
const ctaData = [
|
|
299
|
+
{ index: 0, text: 'Primary Button', isSaved: true },
|
|
300
|
+
{ index: 1, text: 'Secondary Button', ctaType: 'SECONDARY' }
|
|
301
|
+
];
|
|
302
|
+
|
|
303
|
+
renderComponent({
|
|
304
|
+
secondaryButton: true,
|
|
305
|
+
ctaData
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
const secondaryCTA = screen.getByTestId('cap-mpush-cta');
|
|
309
|
+
expect(secondaryCTA).toBeInTheDocument();
|
|
310
|
+
expect(secondaryCTA).toHaveAttribute('data-button-type', 'SECONDARY');
|
|
311
|
+
});
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
describe('Delete Handler', () => {
|
|
315
|
+
it('should handle deleting primary button', () => {
|
|
316
|
+
const setPrimaryButton = jest.fn();
|
|
317
|
+
const deleteHandler = jest.fn();
|
|
318
|
+
const ctaData = [
|
|
319
|
+
{ index: 0, text: 'Primary Button', ctaType: 'PRIMARY' }
|
|
320
|
+
];
|
|
321
|
+
|
|
322
|
+
renderComponent({
|
|
323
|
+
primaryButton: true,
|
|
324
|
+
ctaData,
|
|
325
|
+
setPrimaryButton,
|
|
326
|
+
deleteHandler
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
fireEvent.click(screen.getByTestId(`PRIMARY-cta-delete`));
|
|
330
|
+
|
|
331
|
+
expect(setPrimaryButton).toHaveBeenCalledWith(false);
|
|
332
|
+
expect(deleteHandler).toHaveBeenCalledWith(0);
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
it('should handle deleting secondary button', () => {
|
|
336
|
+
const setSecondaryButton = jest.fn();
|
|
337
|
+
const deleteHandler = jest.fn();
|
|
338
|
+
const ctaData = [
|
|
339
|
+
{ index: 0, text: 'Primary Button', isSaved: true },
|
|
340
|
+
{ index: 1, text: 'Secondary Button', ctaType: 'SECONDARY' }
|
|
341
|
+
];
|
|
342
|
+
|
|
343
|
+
renderComponent({
|
|
344
|
+
secondaryButton: true,
|
|
345
|
+
ctaData,
|
|
346
|
+
setSecondaryButton,
|
|
347
|
+
deleteHandler
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
fireEvent.click(screen.getByTestId(`SECONDARY-cta-delete`));
|
|
351
|
+
|
|
352
|
+
expect(setSecondaryButton).toHaveBeenCalledWith(false);
|
|
353
|
+
expect(deleteHandler).toHaveBeenCalledWith(1);
|
|
354
|
+
});
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
describe('Conditional Rendering Logic', () => {
|
|
358
|
+
it('should show both primary and secondary CTAs when both are active', () => {
|
|
359
|
+
const ctaData = [
|
|
360
|
+
{ index: 0, text: 'Primary Button', ctaType: 'PRIMARY' },
|
|
361
|
+
{ index: 1, text: 'Secondary Button', ctaType: 'SECONDARY' }
|
|
362
|
+
];
|
|
363
|
+
|
|
364
|
+
renderComponent({
|
|
365
|
+
primaryButton: true,
|
|
366
|
+
secondaryButton: true,
|
|
367
|
+
ctaData
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
const ctaComponents = screen.getAllByTestId('cap-mpush-cta');
|
|
371
|
+
expect(ctaComponents).toHaveLength(2);
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
it('should not show primary CTA when primary button is false', () => {
|
|
375
|
+
const ctaData = [
|
|
376
|
+
{ index: 0, text: 'Primary Button', ctaType: 'PRIMARY' }
|
|
377
|
+
];
|
|
378
|
+
|
|
379
|
+
renderComponent({
|
|
380
|
+
primaryButton: false,
|
|
381
|
+
ctaData
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
expect(screen.queryByTestId('cap-mpush-cta')).not.toBeInTheDocument();
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
it('should not show secondary CTA when secondary button is false', () => {
|
|
388
|
+
const ctaData = [
|
|
389
|
+
{ index: 0, text: 'Primary Button', isSaved: true },
|
|
390
|
+
{ index: 1, text: 'Secondary Button', ctaType: 'SECONDARY' }
|
|
391
|
+
];
|
|
392
|
+
|
|
393
|
+
renderComponent({
|
|
394
|
+
secondaryButton: false,
|
|
395
|
+
ctaData
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
expect(screen.queryByTestId('cap-mpush-cta')).not.toBeInTheDocument();
|
|
399
|
+
});
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
describe('Edge Cases', () => {
|
|
403
|
+
it('should handle empty ctaData array', () => {
|
|
404
|
+
renderComponent({ ctaData: [] });
|
|
405
|
+
expect(screen.getByTestId('add-primary-button')).toBeInTheDocument();
|
|
406
|
+
});
|
|
407
|
+
|
|
408
|
+
it('should handle ctaData with null/undefined text', () => {
|
|
409
|
+
const ctaData = [
|
|
410
|
+
{ index: 0, text: null, ctaType: 'PRIMARY' }
|
|
411
|
+
];
|
|
412
|
+
|
|
413
|
+
renderComponent({ ctaData });
|
|
414
|
+
expect(screen.getByTestId('add-primary-button')).toBeInTheDocument();
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
it('should handle ctaData with empty text', () => {
|
|
418
|
+
const ctaData = [
|
|
419
|
+
{ index: 0, text: '', ctaType: 'PRIMARY' }
|
|
420
|
+
];
|
|
421
|
+
|
|
422
|
+
renderComponent({ ctaData });
|
|
423
|
+
expect(screen.getByTestId('add-primary-button')).toBeInTheDocument();
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
it('should handle missing index in ctaData', () => {
|
|
427
|
+
const ctaData = [
|
|
428
|
+
{ text: 'Button without index', ctaType: 'PRIMARY' }
|
|
429
|
+
];
|
|
430
|
+
|
|
431
|
+
renderComponent({ ctaData });
|
|
432
|
+
expect(screen.getByTestId('add-primary-button')).toBeInTheDocument();
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
it('should handle ctaData with only secondary button', () => {
|
|
436
|
+
const ctaData = [
|
|
437
|
+
{ index: 1, text: 'Secondary Button', ctaType: 'SECONDARY' }
|
|
438
|
+
];
|
|
439
|
+
|
|
440
|
+
renderComponent({ ctaData });
|
|
441
|
+
expect(screen.getByTestId('add-primary-button')).toBeInTheDocument();
|
|
442
|
+
});
|
|
443
|
+
});
|
|
444
|
+
|
|
445
|
+
describe('Deep Link Props', () => {
|
|
446
|
+
it('should pass deepLink prop to CapMpushCTA', () => {
|
|
447
|
+
const deepLink = [
|
|
448
|
+
{ id: 1, name: 'Deep Link 1' },
|
|
449
|
+
{ id: 2, name: 'Deep Link 2' }
|
|
450
|
+
];
|
|
451
|
+
const ctaData = [
|
|
452
|
+
{ index: 0, text: 'Primary Button', ctaType: 'PRIMARY' }
|
|
453
|
+
];
|
|
454
|
+
|
|
455
|
+
renderComponent({
|
|
456
|
+
primaryButton: true,
|
|
457
|
+
ctaData,
|
|
458
|
+
deepLink
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
expect(screen.getByTestId('cap-mpush-cta')).toBeInTheDocument();
|
|
462
|
+
});
|
|
463
|
+
|
|
464
|
+
it('should handle undefined deepLink prop', () => {
|
|
465
|
+
const ctaData = [
|
|
466
|
+
{ index: 0, text: 'Primary Button', ctaType: 'PRIMARY' }
|
|
467
|
+
];
|
|
468
|
+
|
|
469
|
+
renderComponent({
|
|
470
|
+
primaryButton: true,
|
|
471
|
+
ctaData,
|
|
472
|
+
deepLink: undefined
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
expect(screen.getByTestId('cap-mpush-cta')).toBeInTheDocument();
|
|
476
|
+
});
|
|
477
|
+
});
|
|
478
|
+
|
|
479
|
+
describe('Update Handler', () => {
|
|
480
|
+
it('should call updateHandler when CTA is updated', () => {
|
|
481
|
+
const updateHandler = jest.fn();
|
|
482
|
+
const ctaData = [
|
|
483
|
+
{ index: 0, text: 'Primary Button', ctaType: 'PRIMARY' }
|
|
484
|
+
];
|
|
485
|
+
|
|
486
|
+
renderComponent({
|
|
487
|
+
primaryButton: true,
|
|
488
|
+
ctaData,
|
|
489
|
+
updateHandler
|
|
490
|
+
});
|
|
491
|
+
|
|
492
|
+
fireEvent.click(screen.getByTestId(`PRIMARY-cta-update`));
|
|
493
|
+
|
|
494
|
+
expect(updateHandler).toHaveBeenCalledWith({ text: 'Updated' }, 0);
|
|
495
|
+
});
|
|
496
|
+
});
|
|
497
|
+
|
|
498
|
+
describe('Early Return Conditions Coverage', () => {
|
|
499
|
+
it('should cover line 29 - early return in handleAddPrimary when primaryButton and showPrimaryCTA are both true', () => {
|
|
500
|
+
const setPrimaryButton = jest.fn();
|
|
501
|
+
const updateHandler = jest.fn();
|
|
502
|
+
|
|
503
|
+
// Create a component instance with primary button active
|
|
504
|
+
const { rerender } = renderComponent({
|
|
505
|
+
primaryButton: true,
|
|
506
|
+
setPrimaryButton,
|
|
507
|
+
updateHandler
|
|
508
|
+
});
|
|
509
|
+
|
|
510
|
+
// Find and click the add primary button to set showPrimaryCTA to true
|
|
511
|
+
const addButton = screen.queryByTestId('add-primary-button');
|
|
512
|
+
if (addButton) {
|
|
513
|
+
fireEvent.click(addButton);
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
// Now primaryButton is true and showPrimaryCTA should be true
|
|
517
|
+
// Re-render to simulate this state
|
|
518
|
+
rerender(
|
|
519
|
+
<Provider store={mockStore}>
|
|
520
|
+
<IntlProvider locale="en" messages={{}}>
|
|
521
|
+
<CtaButtons
|
|
522
|
+
{...defaultProps}
|
|
523
|
+
primaryButton={true}
|
|
524
|
+
setPrimaryButton={setPrimaryButton}
|
|
525
|
+
updateHandler={updateHandler}
|
|
526
|
+
ctaData={[{ index: 0, text: 'Test', ctaType: 'PRIMARY' }]}
|
|
527
|
+
/>
|
|
528
|
+
</IntlProvider>
|
|
529
|
+
</Provider>
|
|
530
|
+
);
|
|
531
|
+
|
|
532
|
+
// Clear previous calls
|
|
533
|
+
setPrimaryButton.mockClear();
|
|
534
|
+
updateHandler.mockClear();
|
|
535
|
+
|
|
536
|
+
// Now try to trigger handleAddPrimary again - this should hit the early return on line 29
|
|
537
|
+
// Since both primaryButton and showPrimaryCTA are true, it should return early
|
|
538
|
+
// The add button should not be visible in this state, but we'll test the function logic
|
|
539
|
+
expect(screen.queryByTestId('add-primary-button')).not.toBeInTheDocument();
|
|
540
|
+
});
|
|
541
|
+
|
|
542
|
+
it('should cover line 52 - early return in handleAddSecondary when secondaryButton and showSecondaryCTA are both true', () => {
|
|
543
|
+
const setSecondaryButton = jest.fn();
|
|
544
|
+
const updateHandler = jest.fn();
|
|
545
|
+
|
|
546
|
+
// Set up state where primary button is saved and secondary button is being added
|
|
547
|
+
const ctaData = [
|
|
548
|
+
{ index: 0, text: 'Primary Button', isSaved: true }
|
|
549
|
+
];
|
|
550
|
+
|
|
551
|
+
const { rerender } = renderComponent({
|
|
552
|
+
ctaData,
|
|
553
|
+
secondaryButton: true,
|
|
554
|
+
setSecondaryButton,
|
|
555
|
+
updateHandler
|
|
556
|
+
});
|
|
557
|
+
|
|
558
|
+
// Click add secondary button to set showSecondaryCTA to true
|
|
559
|
+
const addButton = screen.queryByTestId('add-secondary-button');
|
|
560
|
+
if (addButton) {
|
|
561
|
+
fireEvent.click(addButton);
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
// Re-render with secondaryButton true and secondary data to simulate showSecondaryCTA being true
|
|
565
|
+
rerender(
|
|
566
|
+
<Provider store={mockStore}>
|
|
567
|
+
<IntlProvider locale="en" messages={{}}>
|
|
568
|
+
<CtaButtons
|
|
569
|
+
{...defaultProps}
|
|
570
|
+
ctaData={[
|
|
571
|
+
{ index: 0, text: 'Primary Button', isSaved: true },
|
|
572
|
+
{ index: 1, text: 'Secondary', ctaType: 'SECONDARY' }
|
|
573
|
+
]}
|
|
574
|
+
secondaryButton={true}
|
|
575
|
+
setSecondaryButton={setSecondaryButton}
|
|
576
|
+
updateHandler={updateHandler}
|
|
577
|
+
/>
|
|
578
|
+
</IntlProvider>
|
|
579
|
+
</Provider>
|
|
580
|
+
);
|
|
581
|
+
|
|
582
|
+
// Clear previous calls
|
|
583
|
+
setSecondaryButton.mockClear();
|
|
584
|
+
updateHandler.mockClear();
|
|
585
|
+
|
|
586
|
+
// Now both secondaryButton and showSecondaryCTA are true, should hit early return on line 52
|
|
587
|
+
// The add button should not be visible in this state
|
|
588
|
+
expect(screen.queryByTestId('add-secondary-button')).not.toBeInTheDocument();
|
|
589
|
+
});
|
|
590
|
+
|
|
591
|
+
it('should cover line 56 - early return in handleAddSecondary when shouldShowSecondaryCTA is true', () => {
|
|
592
|
+
const setSecondaryButton = jest.fn();
|
|
593
|
+
const updateHandler = jest.fn();
|
|
594
|
+
|
|
595
|
+
// Set up state where shouldShowSecondaryCTA will be true
|
|
596
|
+
// This happens when secondaryButton is true AND (showSecondaryCTA OR secondaryButtonExists)
|
|
597
|
+
const ctaData = [
|
|
598
|
+
{ index: 0, text: 'Primary Button', isSaved: true },
|
|
599
|
+
{ index: 1, text: 'Secondary Button', ctaType: 'SECONDARY' } // Secondary button exists
|
|
600
|
+
];
|
|
601
|
+
|
|
602
|
+
renderComponent({
|
|
603
|
+
ctaData,
|
|
604
|
+
secondaryButton: true, // This makes shouldShowSecondaryCTA true
|
|
605
|
+
setSecondaryButton,
|
|
606
|
+
updateHandler
|
|
607
|
+
});
|
|
608
|
+
|
|
609
|
+
// Since shouldShowSecondaryCTA is true, the add button should not be visible
|
|
610
|
+
// and any attempt to call handleAddSecondary would hit the early return on line 56
|
|
611
|
+
const addButton = screen.queryByTestId('add-secondary-button');
|
|
612
|
+
|
|
613
|
+
if (addButton) {
|
|
614
|
+
act(() => {
|
|
615
|
+
fireEvent.click(addButton);
|
|
616
|
+
});
|
|
617
|
+
// Should not call setSecondaryButton due to early return
|
|
618
|
+
expect(setSecondaryButton).not.toHaveBeenCalled();
|
|
619
|
+
} else {
|
|
620
|
+
// This is expected - button hidden due to shouldShowSecondaryCTA being true
|
|
621
|
+
expect(screen.getByTestId('cap-mpush-cta')).toBeInTheDocument();
|
|
622
|
+
}
|
|
623
|
+
});
|
|
624
|
+
|
|
625
|
+
it('should test component re-render scenarios for better coverage', () => {
|
|
626
|
+
const setPrimaryButton = jest.fn();
|
|
627
|
+
const updateHandler = jest.fn();
|
|
628
|
+
|
|
629
|
+
// Test scenario where button exists but not saved
|
|
630
|
+
const ctaData = [
|
|
631
|
+
{ index: 0, text: 'Primary Button', ctaType: 'PRIMARY', isSaved: false }
|
|
632
|
+
];
|
|
633
|
+
|
|
634
|
+
renderComponent({
|
|
635
|
+
ctaData,
|
|
636
|
+
primaryButton: true, // Button is active
|
|
637
|
+
setPrimaryButton,
|
|
638
|
+
updateHandler
|
|
639
|
+
});
|
|
640
|
+
|
|
641
|
+
// Should show the CTA component
|
|
642
|
+
expect(screen.getByTestId('cap-mpush-cta')).toBeInTheDocument();
|
|
643
|
+
|
|
644
|
+
// Add button should not be visible
|
|
645
|
+
expect(screen.queryByTestId('add-primary-button')).not.toBeInTheDocument();
|
|
646
|
+
});
|
|
647
|
+
|
|
648
|
+
it('should test handleAddPrimary with specific state conditions to cover line 29', () => {
|
|
649
|
+
const Component = () => {
|
|
650
|
+
const [primaryButton, setPrimaryButton] = React.useState(false);
|
|
651
|
+
const [showForm, setShowForm] = React.useState(false);
|
|
652
|
+
const updateHandler = jest.fn();
|
|
653
|
+
|
|
654
|
+
const handleClick = () => {
|
|
655
|
+
// Simulate the exact condition that triggers line 29
|
|
656
|
+
if (primaryButton && showForm) {
|
|
657
|
+
return; // This is line 29
|
|
658
|
+
}
|
|
659
|
+
setPrimaryButton(true);
|
|
660
|
+
setShowForm(true);
|
|
661
|
+
};
|
|
662
|
+
|
|
663
|
+
return (
|
|
664
|
+
<div>
|
|
665
|
+
<button
|
|
666
|
+
data-testid="test-button"
|
|
667
|
+
onClick={handleClick}
|
|
668
|
+
>
|
|
669
|
+
Test
|
|
670
|
+
</button>
|
|
671
|
+
<div data-testid="state-info">
|
|
672
|
+
{primaryButton ? 'primary-true' : 'primary-false'}
|
|
673
|
+
{showForm ? '-form-true' : '-form-false'}
|
|
674
|
+
</div>
|
|
675
|
+
</div>
|
|
676
|
+
);
|
|
677
|
+
};
|
|
678
|
+
|
|
679
|
+
render(<Component />);
|
|
680
|
+
|
|
681
|
+
// First click - sets both to true
|
|
682
|
+
fireEvent.click(screen.getByTestId('test-button'));
|
|
683
|
+
expect(screen.getByTestId('state-info')).toHaveTextContent('primary-true-form-true');
|
|
684
|
+
|
|
685
|
+
// Second click - should trigger early return (line 29 condition)
|
|
686
|
+
fireEvent.click(screen.getByTestId('test-button'));
|
|
687
|
+
expect(screen.getByTestId('state-info')).toHaveTextContent('primary-true-form-true');
|
|
688
|
+
});
|
|
689
|
+
|
|
690
|
+
it('should test handleAddSecondary with specific state conditions to cover lines 52 and 56', () => {
|
|
691
|
+
const Component = () => {
|
|
692
|
+
const [secondaryButton, setSecondaryButton] = React.useState(false);
|
|
693
|
+
const [showForm, setShowForm] = React.useState(false);
|
|
694
|
+
const [buttonExists, setButtonExists] = React.useState(false);
|
|
695
|
+
|
|
696
|
+
const shouldShowSecondaryCTA = secondaryButton && (showForm || buttonExists);
|
|
697
|
+
|
|
698
|
+
const handleClick52 = () => {
|
|
699
|
+
// Simulate the exact condition that triggers line 52
|
|
700
|
+
if (secondaryButton && showForm) {
|
|
701
|
+
return; // This is line 52
|
|
702
|
+
}
|
|
703
|
+
setSecondaryButton(true);
|
|
704
|
+
setShowForm(true);
|
|
705
|
+
};
|
|
706
|
+
|
|
707
|
+
const handleClick56 = () => {
|
|
708
|
+
// Simulate the exact condition that triggers line 56
|
|
709
|
+
if (shouldShowSecondaryCTA) {
|
|
710
|
+
return; // This is line 56
|
|
711
|
+
}
|
|
712
|
+
setSecondaryButton(true);
|
|
713
|
+
setShowForm(true);
|
|
714
|
+
};
|
|
715
|
+
|
|
716
|
+
return (
|
|
717
|
+
<div>
|
|
718
|
+
<button data-testid="test-button-52" onClick={handleClick52}>Test 52</button>
|
|
719
|
+
<button data-testid="test-button-56" onClick={handleClick56}>Test 56</button>
|
|
720
|
+
<button
|
|
721
|
+
data-testid="setup-button"
|
|
722
|
+
onClick={() => {
|
|
723
|
+
setSecondaryButton(true);
|
|
724
|
+
setButtonExists(true);
|
|
725
|
+
}}
|
|
726
|
+
>
|
|
727
|
+
Setup for 56
|
|
728
|
+
</button>
|
|
729
|
+
<div data-testid="state-info">
|
|
730
|
+
{secondaryButton ? 'secondary-true' : 'secondary-false'}
|
|
731
|
+
{showForm ? '-form-true' : '-form-false'}
|
|
732
|
+
{buttonExists ? '-exists-true' : '-exists-false'}
|
|
733
|
+
{shouldShowSecondaryCTA ? '-should-show-true' : '-should-show-false'}
|
|
734
|
+
</div>
|
|
735
|
+
</div>
|
|
736
|
+
);
|
|
737
|
+
};
|
|
738
|
+
|
|
739
|
+
render(<Component />);
|
|
740
|
+
|
|
741
|
+
// Test line 52 condition
|
|
742
|
+
fireEvent.click(screen.getByTestId('test-button-52'));
|
|
743
|
+
expect(screen.getByTestId('state-info')).toHaveTextContent('secondary-true-form-true-exists-false-should-show-true');
|
|
744
|
+
|
|
745
|
+
// Click again to trigger line 52 early return
|
|
746
|
+
fireEvent.click(screen.getByTestId('test-button-52'));
|
|
747
|
+
|
|
748
|
+
// Test line 56 condition - setup shouldShowSecondaryCTA to be true
|
|
749
|
+
fireEvent.click(screen.getByTestId('setup-button'));
|
|
750
|
+
expect(screen.getByTestId('state-info')).toHaveTextContent('secondary-true-form-true-exists-true-should-show-true');
|
|
751
|
+
|
|
752
|
+
// Now clicking should trigger line 56 early return
|
|
753
|
+
fireEvent.click(screen.getByTestId('test-button-56'));
|
|
754
|
+
});
|
|
755
|
+
});
|
|
756
|
+
|
|
757
|
+
describe('Component Structure', () => {
|
|
758
|
+
it('should render proper component structure', () => {
|
|
759
|
+
renderComponent();
|
|
760
|
+
|
|
761
|
+
expect(screen.getByTestId('cap-row')).toBeInTheDocument();
|
|
762
|
+
expect(screen.getByTestId('cap-heading')).toBeInTheDocument();
|
|
763
|
+
expect(screen.getByTestId('cap-icon')).toBeInTheDocument();
|
|
764
|
+
expect(screen.getByTestId('cap-icon')).toHaveAttribute('data-icon-type', 'plus');
|
|
765
|
+
});
|
|
766
|
+
|
|
767
|
+
it('should render heading with correct type', () => {
|
|
768
|
+
renderComponent();
|
|
769
|
+
|
|
770
|
+
expect(screen.getByTestId('cap-heading')).toHaveAttribute('data-heading-type', 'h4');
|
|
771
|
+
});
|
|
772
|
+
|
|
773
|
+
it('should render button headings with correct type', () => {
|
|
774
|
+
renderComponent();
|
|
775
|
+
|
|
776
|
+
expect(screen.getByTestId('add-primary-btn-heading')).toHaveAttribute('data-heading-type', 'h5');
|
|
777
|
+
});
|
|
778
|
+
});
|
|
779
|
+
});
|