@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.
Files changed (78) hide show
  1. package/containers/App/constants.js +1 -0
  2. package/index.html +3 -1
  3. package/package.json +1 -1
  4. package/services/api.js +4 -4
  5. package/tests/integration/TemplateCreation/TemplateCreation.integration.test.js +8 -3
  6. package/tests/integration/TemplateCreation/api-response.js +5 -0
  7. package/tests/integration/TemplateCreation/msw-handler.js +42 -63
  8. package/utils/common.js +7 -0
  9. package/utils/commonUtils.js +2 -6
  10. package/utils/createPayload.js +272 -0
  11. package/utils/tests/createPayload.test.js +761 -0
  12. package/v2Components/CapImageUpload/index.js +59 -46
  13. package/v2Components/CapInAppCTA/index.js +1 -0
  14. package/v2Components/CapMpushCTA/constants.js +25 -0
  15. package/v2Components/CapMpushCTA/index.js +332 -0
  16. package/v2Components/CapMpushCTA/index.scss +95 -0
  17. package/v2Components/CapMpushCTA/messages.js +89 -0
  18. package/v2Components/CapTagList/index.js +177 -120
  19. package/v2Components/CapVideoUpload/constants.js +3 -0
  20. package/v2Components/CapVideoUpload/index.js +167 -110
  21. package/v2Components/CapVideoUpload/messages.js +16 -0
  22. package/v2Components/Carousel/index.js +15 -13
  23. package/v2Components/CustomerSearchSection/index.js +12 -7
  24. package/v2Components/ErrorInfoNote/style.scss +1 -0
  25. package/v2Components/MobilePushPreviewV2/index.js +37 -5
  26. package/v2Components/TemplatePreview/_templatePreview.scss +114 -72
  27. package/v2Components/TemplatePreview/assets/images/Android _ With date and time.svg +29 -0
  28. package/v2Components/TemplatePreview/assets/images/android.svg +9 -0
  29. package/v2Components/TemplatePreview/assets/images/iOS _ With date and time.svg +26 -0
  30. package/v2Components/TemplatePreview/assets/images/ios.svg +9 -0
  31. package/v2Components/TemplatePreview/index.js +178 -50
  32. package/v2Components/TemplatePreview/messages.js +4 -0
  33. package/v2Components/TestAndPreviewSlidebox/CustomValuesEditor.js +169 -0
  34. package/v2Components/TestAndPreviewSlidebox/LeftPanelContent.js +95 -0
  35. package/v2Components/TestAndPreviewSlidebox/PreviewSection.js +69 -0
  36. package/v2Components/TestAndPreviewSlidebox/SendTestMessage.js +68 -0
  37. package/v2Components/TestAndPreviewSlidebox/index.js +67 -246
  38. package/v2Components/TestAndPreviewSlidebox/tests/CustomValuesEditor.test.js +425 -0
  39. package/v2Components/TestAndPreviewSlidebox/tests/LeftPanelContent.test.js +400 -0
  40. package/v2Components/TestAndPreviewSlidebox/tests/SendTestMessage.test.js +448 -0
  41. package/v2Containers/CreativesContainer/SlideBoxContent.js +9 -9
  42. package/v2Containers/CreativesContainer/index.js +191 -136
  43. package/v2Containers/Email/index.js +15 -2
  44. package/v2Containers/InApp/constants.js +1 -0
  45. package/v2Containers/InApp/index.js +13 -13
  46. package/v2Containers/MobilePush/Create/index.js +1 -0
  47. package/v2Containers/MobilePush/commonMethods.js +7 -14
  48. package/v2Containers/MobilePushNew/actions.js +116 -0
  49. package/v2Containers/MobilePushNew/components/CtaButtons.js +170 -0
  50. package/v2Containers/MobilePushNew/components/MediaUploaders.js +754 -0
  51. package/v2Containers/MobilePushNew/components/PlatformContentFields.js +279 -0
  52. package/v2Containers/MobilePushNew/components/index.js +5 -0
  53. package/v2Containers/MobilePushNew/components/tests/CtaButtons.test.js +779 -0
  54. package/v2Containers/MobilePushNew/components/tests/MediaUploaders.test.js +2114 -0
  55. package/v2Containers/MobilePushNew/components/tests/PlatformContentFields.test.js +343 -0
  56. package/v2Containers/MobilePushNew/constants.js +115 -0
  57. package/v2Containers/MobilePushNew/hooks/tests/usePlatformSync.test.js +1299 -0
  58. package/v2Containers/MobilePushNew/hooks/tests/useUpload.test.js +1223 -0
  59. package/v2Containers/MobilePushNew/hooks/usePlatformSync.js +246 -0
  60. package/v2Containers/MobilePushNew/hooks/useUpload.js +726 -0
  61. package/v2Containers/MobilePushNew/index.js +2280 -0
  62. package/v2Containers/MobilePushNew/index.scss +308 -0
  63. package/v2Containers/MobilePushNew/messages.js +226 -0
  64. package/v2Containers/MobilePushNew/reducer.js +160 -0
  65. package/v2Containers/MobilePushNew/sagas.js +198 -0
  66. package/v2Containers/MobilePushNew/selectors.js +55 -0
  67. package/v2Containers/MobilePushNew/tests/reducer.test.js +741 -0
  68. package/v2Containers/MobilePushNew/tests/sagas.test.js +863 -0
  69. package/v2Containers/MobilePushNew/tests/selectors.test.js +425 -0
  70. package/v2Containers/MobilePushNew/tests/utils.test.js +322 -0
  71. package/v2Containers/MobilePushNew/utils.js +33 -0
  72. package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +5 -5
  73. package/v2Containers/TagList/index.js +56 -10
  74. package/v2Containers/Templates/_templates.scss +101 -1
  75. package/v2Containers/Templates/index.js +147 -35
  76. package/v2Containers/Templates/messages.js +8 -0
  77. package/v2Containers/Templates/sagas.js +2 -0
  78. package/v2Containers/Whatsapp/constants.js +1 -0
@@ -0,0 +1,400 @@
1
+ import React from 'react';
2
+ import {
3
+ render, screen, fireEvent,
4
+ } from '@testing-library/react';
5
+ import { IntlProvider } from 'react-intl';
6
+ import PropTypes from 'prop-types';
7
+ import LeftPanelContent from '../LeftPanelContent';
8
+
9
+ // Mock the CustomerSearchSection component
10
+ jest.mock('../../CustomerSearchSection', () => {
11
+ // eslint-disable-next-line react/prop-types
12
+ const MockCustomerSearchSection = ({ onCustomerSelect, onSearch, onClearSelection }) => (
13
+ <div data-testid="customer-search-section">
14
+ <button type="button" onClick={() => onCustomerSelect({ id: 1, name: 'Test Customer' })}>
15
+ Select Customer
16
+ </button>
17
+ <button type="button" onClick={() => onSearch('test')}>Search</button>
18
+ <button type="button" onClick={onClearSelection}>Clear Selection</button>
19
+ </div>
20
+ );
21
+ return MockCustomerSearchSection;
22
+ });
23
+
24
+ // Mock messages for testing
25
+ const mockMessages = {
26
+ extractingTags: { id: 'extractingTags', defaultMessage: 'Extracting tags...' },
27
+ customerSearchTitle: { id: 'customerSearchTitle', defaultMessage: 'Customer' },
28
+ enterCustomValuesForTags: { id: 'enterCustomValuesForTags', defaultMessage: 'Enter custom values for tags' },
29
+ noTagsExtracted: { id: 'noTagsExtracted', defaultMessage: 'There are no personalization tags in this message, you can directly send out a test message' },
30
+ };
31
+
32
+ // Test wrapper with IntlProvider
33
+ const TestWrapper = ({ children, locale = 'en' }) => (
34
+ <IntlProvider locale={locale} messages={mockMessages}>
35
+ {children}
36
+ </IntlProvider>
37
+ );
38
+
39
+ TestWrapper.propTypes = {
40
+ children: PropTypes.node.isRequired,
41
+ locale: PropTypes.string,
42
+ };
43
+
44
+ TestWrapper.defaultProps = {
45
+ locale: 'en',
46
+ };
47
+
48
+ // Default props for testing
49
+ const defaultProps = {
50
+ isExtractingTags: false,
51
+ extractedTags: [
52
+ { name: 'user', fullPath: 'user.name' },
53
+ { name: 'email', fullPath: 'user.email' },
54
+ ],
55
+ selectedCustomer: null,
56
+ handleCustomerSelect: jest.fn(),
57
+ handleSearchCustomer: jest.fn(),
58
+ customers: [
59
+ { id: 1, name: 'John Doe', email: 'john@example.com' },
60
+ { id: 2, name: 'Jane Smith', email: 'jane@example.com' },
61
+ ],
62
+ isSearchingCustomer: false,
63
+ handleClearSelection: jest.fn(),
64
+ tagsExtracted: false,
65
+ handleExtractTags: jest.fn(),
66
+ renderCustomValuesEditor: jest.fn(() => <div data-testid="custom-values-editor">Custom Values Editor</div>),
67
+ };
68
+
69
+ describe('LeftPanelContent', () => {
70
+ beforeEach(() => {
71
+ jest.clearAllMocks();
72
+ });
73
+
74
+ describe('Loading State', () => {
75
+ it('should render loading spinner when extracting tags', () => {
76
+ const { container } = render(
77
+ <TestWrapper>
78
+ <LeftPanelContent {...defaultProps} isExtractingTags />
79
+ </TestWrapper>
80
+ );
81
+
82
+ expect(screen.getByText('Extracting tags...')).toBeTruthy();
83
+ expect(container.querySelector('.ant-spin')).toBeTruthy();
84
+ expect(container.querySelector('.loading-container')).toBeTruthy();
85
+ });
86
+
87
+ it('should not render other content when extracting tags', () => {
88
+ render(
89
+ <TestWrapper>
90
+ <LeftPanelContent {...defaultProps} isExtractingTags />
91
+ </TestWrapper>
92
+ );
93
+
94
+ expect(screen.queryByTestId('customer-search-section')).toBeNull();
95
+ expect(screen.queryByText('Enter custom values for tags')).toBeNull();
96
+ expect(screen.queryByText('No personalization tags found in the template')).toBeNull();
97
+ });
98
+ });
99
+
100
+ describe('Main Content with Extracted Tags', () => {
101
+ it('should render customer search section when tags are extracted', () => {
102
+ render(
103
+ <TestWrapper>
104
+ <LeftPanelContent {...defaultProps} />
105
+ </TestWrapper>
106
+ );
107
+
108
+ expect(screen.getByText('Customer')).toBeTruthy();
109
+ expect(screen.getByTestId('customer-search-section')).toBeTruthy();
110
+ });
111
+
112
+ it('should render extract tags button when tags not yet extracted', () => {
113
+ render(
114
+ <TestWrapper>
115
+ <LeftPanelContent {...defaultProps} tagsExtracted={false} />
116
+ </TestWrapper>
117
+ );
118
+
119
+ expect(screen.getByText('Enter custom values for tags')).toBeTruthy();
120
+ expect(screen.getByRole('button', { name: /enter custom values for tags/i })).toBeTruthy();
121
+ });
122
+
123
+ it('should not render extract tags button when tags are already extracted', () => {
124
+ render(
125
+ <TestWrapper>
126
+ <LeftPanelContent {...defaultProps} tagsExtracted />
127
+ </TestWrapper>
128
+ );
129
+
130
+ expect(screen.queryByText('Enter custom values for tags')).toBeNull();
131
+ });
132
+
133
+ it('should render custom values editor when tags are extracted', () => {
134
+ render(
135
+ <TestWrapper>
136
+ <LeftPanelContent {...defaultProps} tagsExtracted />
137
+ </TestWrapper>
138
+ );
139
+
140
+ // Check that renderCustomValuesEditor function is called
141
+ expect(defaultProps.renderCustomValuesEditor).toHaveBeenCalled();
142
+ // Since the actual rendering depends on what renderCustomValuesEditor returns,
143
+ // we verify the function was called which means the condition was met
144
+ expect(screen.getByTestId('customer-search-section')).toBeTruthy();
145
+ });
146
+
147
+ it('should not render custom values editor when tags are not extracted', () => {
148
+ render(
149
+ <TestWrapper>
150
+ <LeftPanelContent {...defaultProps} tagsExtracted={false} />
151
+ </TestWrapper>
152
+ );
153
+
154
+ expect(screen.queryByTestId('custom-values-editor')).toBeNull();
155
+ expect(defaultProps.renderCustomValuesEditor).not.toHaveBeenCalled();
156
+ });
157
+ });
158
+
159
+ describe('No Tags State', () => {
160
+ it('should render info note when no tags are extracted', () => {
161
+ render(
162
+ <TestWrapper>
163
+ <LeftPanelContent {...defaultProps} extractedTags={[]} />
164
+ </TestWrapper>
165
+ );
166
+
167
+ expect(screen.getByText('There are no personalization tags in this message, you can directly send out a test message')).toBeTruthy();
168
+ expect(screen.queryByTestId('customer-search-section')).toBeNull();
169
+ expect(screen.queryByText('Enter custom values for tags')).toBeNull();
170
+ });
171
+
172
+ it('should not render customer search or other content when no tags', () => {
173
+ render(
174
+ <TestWrapper>
175
+ <LeftPanelContent {...defaultProps} extractedTags={[]} />
176
+ </TestWrapper>
177
+ );
178
+
179
+ expect(screen.queryByText('Customer Search')).toBeNull();
180
+ expect(screen.queryByTestId('customer-search-section')).toBeNull();
181
+ expect(screen.queryByTestId('custom-values-editor')).toBeNull();
182
+ });
183
+ });
184
+
185
+ describe('Customer Search Interactions', () => {
186
+ it('should call handleCustomerSelect when customer is selected', () => {
187
+ render(
188
+ <TestWrapper>
189
+ <LeftPanelContent {...defaultProps} />
190
+ </TestWrapper>
191
+ );
192
+
193
+ const selectButton = screen.getByText('Select Customer');
194
+ fireEvent.click(selectButton);
195
+
196
+ expect(defaultProps.handleCustomerSelect).toHaveBeenCalledWith({ id: 1, name: 'Test Customer' });
197
+ });
198
+
199
+ it('should call handleSearchCustomer when search is performed', () => {
200
+ render(
201
+ <TestWrapper>
202
+ <LeftPanelContent {...defaultProps} />
203
+ </TestWrapper>
204
+ );
205
+
206
+ const searchButton = screen.getByText('Search');
207
+ fireEvent.click(searchButton);
208
+
209
+ expect(defaultProps.handleSearchCustomer).toHaveBeenCalledWith('test');
210
+ });
211
+
212
+ it('should call handleClearSelection when clear is clicked', () => {
213
+ render(
214
+ <TestWrapper>
215
+ <LeftPanelContent {...defaultProps} />
216
+ </TestWrapper>
217
+ );
218
+
219
+ const clearButton = screen.getByText('Clear Selection');
220
+ fireEvent.click(clearButton);
221
+
222
+ expect(defaultProps.handleClearSelection).toHaveBeenCalled();
223
+ });
224
+ });
225
+
226
+ describe('Tags Extraction', () => {
227
+ it('should call handleExtractTags when extract tags button is clicked', () => {
228
+ render(
229
+ <TestWrapper>
230
+ <LeftPanelContent {...defaultProps} tagsExtracted={false} />
231
+ </TestWrapper>
232
+ );
233
+
234
+ const extractButton = screen.getByText('Enter custom values for tags');
235
+ fireEvent.click(extractButton);
236
+
237
+ expect(defaultProps.handleExtractTags).toHaveBeenCalled();
238
+ });
239
+
240
+ it('should have proper button styling for extract tags button', () => {
241
+ const { container } = render(
242
+ <TestWrapper>
243
+ <LeftPanelContent {...defaultProps} tagsExtracted={false} />
244
+ </TestWrapper>
245
+ );
246
+
247
+ const buttonContainer = container.querySelector('.extract-tags-button');
248
+ expect(buttonContainer).toBeTruthy();
249
+ });
250
+ });
251
+
252
+ describe('Component Structure', () => {
253
+ it('should have proper CSS classes for sections', () => {
254
+ const { container } = render(
255
+ <TestWrapper>
256
+ <LeftPanelContent {...defaultProps} />
257
+ </TestWrapper>
258
+ );
259
+
260
+ expect(container.querySelector('.panel-section.customer-section')).toBeTruthy();
261
+ expect(container.querySelector('.panel-section')).toBeTruthy();
262
+ });
263
+
264
+ it('should render sections in correct order', () => {
265
+ render(
266
+ <TestWrapper>
267
+ <LeftPanelContent {...defaultProps} tagsExtracted={false} />
268
+ </TestWrapper>
269
+ );
270
+
271
+ const customerTitle = screen.getByText('Customer');
272
+ const extractButton = screen.getByText('Enter custom values for tags');
273
+
274
+ // Customer search should appear before extract tags button
275
+ expect(customerTitle.compareDocumentPosition(extractButton)).toBe(Node.DOCUMENT_POSITION_FOLLOWING);
276
+ });
277
+ });
278
+
279
+ describe('Props Validation', () => {
280
+ it('should handle selectedCustomer prop correctly', () => {
281
+ const selectedCustomer = { id: 1, name: 'John Doe', email: 'john@example.com' };
282
+
283
+ render(
284
+ <TestWrapper>
285
+ <LeftPanelContent {...defaultProps} selectedCustomer={selectedCustomer} />
286
+ </TestWrapper>
287
+ );
288
+
289
+ // Component should render without errors with selectedCustomer
290
+ expect(screen.getByTestId('customer-search-section')).toBeTruthy();
291
+ });
292
+
293
+ it('should handle isSearchingCustomer prop correctly', () => {
294
+ render(
295
+ <TestWrapper>
296
+ <LeftPanelContent {...defaultProps} isSearchingCustomer />
297
+ </TestWrapper>
298
+ );
299
+
300
+ // Component should render without errors with isSearchingCustomer true
301
+ expect(screen.getByTestId('customer-search-section')).toBeTruthy();
302
+ });
303
+
304
+ it('should accept all required props without warnings', () => {
305
+ const consoleSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
306
+
307
+ render(
308
+ <TestWrapper>
309
+ <LeftPanelContent {...defaultProps} />
310
+ </TestWrapper>
311
+ );
312
+
313
+ expect(consoleSpy).not.toHaveBeenCalled();
314
+ consoleSpy.mockRestore();
315
+ });
316
+ });
317
+
318
+ describe('Edge Cases', () => {
319
+ it('should handle empty customers array', () => {
320
+ render(
321
+ <TestWrapper>
322
+ <LeftPanelContent {...defaultProps} customers={[]} />
323
+ </TestWrapper>
324
+ );
325
+
326
+ expect(screen.getByTestId('customer-search-section')).toBeTruthy();
327
+ });
328
+
329
+ it('should handle single tag in extractedTags', () => {
330
+ const singleTag = [{ name: 'user', fullPath: 'user.name' }];
331
+
332
+ render(
333
+ <TestWrapper>
334
+ <LeftPanelContent {...defaultProps} extractedTags={singleTag} />
335
+ </TestWrapper>
336
+ );
337
+
338
+ expect(screen.getByText('Customer')).toBeTruthy();
339
+ expect(screen.getByText('Enter custom values for tags')).toBeTruthy();
340
+ });
341
+
342
+ it('should handle null extractedTags gracefully', () => {
343
+ render(
344
+ <TestWrapper>
345
+ <LeftPanelContent {...defaultProps} extractedTags={null} />
346
+ </TestWrapper>
347
+ );
348
+
349
+ expect(screen.getByText('There are no personalization tags in this message, you can directly send out a test message')).toBeTruthy();
350
+ });
351
+
352
+ it('should handle renderCustomValuesEditor returning null', () => {
353
+ const nullRenderer = jest.fn(() => null);
354
+
355
+ render(
356
+ <TestWrapper>
357
+ <LeftPanelContent {...defaultProps} tagsExtracted renderCustomValuesEditor={nullRenderer} />
358
+ </TestWrapper>
359
+ );
360
+
361
+ expect(nullRenderer).toHaveBeenCalled();
362
+ expect(screen.queryByTestId('custom-values-editor')).toBeNull();
363
+ });
364
+ });
365
+
366
+ describe('Accessibility', () => {
367
+ it('should have proper heading structure', () => {
368
+ render(
369
+ <TestWrapper>
370
+ <LeftPanelContent {...defaultProps} />
371
+ </TestWrapper>
372
+ );
373
+
374
+ // CapHeader should render the customer search title
375
+ expect(screen.getByText('Customer')).toBeTruthy();
376
+ });
377
+
378
+ it('should have proper button roles', () => {
379
+ render(
380
+ <TestWrapper>
381
+ <LeftPanelContent {...defaultProps} tagsExtracted={false} />
382
+ </TestWrapper>
383
+ );
384
+
385
+ const buttons = screen.getAllByRole('button');
386
+ expect(buttons.length).toBeGreaterThan(0);
387
+ });
388
+
389
+ it('should have informative text for no tags state', () => {
390
+ render(
391
+ <TestWrapper>
392
+ <LeftPanelContent {...defaultProps} extractedTags={[]} />
393
+ </TestWrapper>
394
+ );
395
+
396
+ const infoText = screen.getByText('There are no personalization tags in this message, you can directly send out a test message');
397
+ expect(infoText).toBeTruthy();
398
+ });
399
+ });
400
+ });