@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.
Files changed (153) hide show
  1. package/assets/Android.png +0 -0
  2. package/assets/iOS.png +0 -0
  3. package/constants/unified.js +2 -1
  4. package/initialReducer.js +2 -0
  5. package/package.json +1 -1
  6. package/services/api.js +10 -0
  7. package/services/tests/api.test.js +34 -0
  8. package/tests/integration/TemplateCreation/TemplateCreation.integration.test.js +17 -35
  9. package/tests/integration/TemplateCreation/api-response.js +31 -1
  10. package/tests/integration/TemplateCreation/msw-handler.js +2 -0
  11. package/utils/common.js +5 -0
  12. package/utils/commonUtils.js +28 -5
  13. package/utils/imageUrlUpload.js +13 -14
  14. package/utils/tests/commonUtil.test.js +224 -0
  15. package/utils/tests/imageUrlUpload.test.js +298 -0
  16. package/utils/transformTemplateConfig.js +0 -10
  17. package/v2Components/CapDeviceContent/index.js +61 -56
  18. package/v2Components/CapTagList/index.js +6 -1
  19. package/v2Components/CapTagListWithInput/index.js +5 -1
  20. package/v2Components/CapTagListWithInput/messages.js +1 -1
  21. package/v2Components/CapWhatsappCTA/tests/index.test.js +5 -0
  22. package/v2Components/ErrorInfoNote/constants.js +1 -0
  23. package/v2Components/ErrorInfoNote/index.js +402 -72
  24. package/v2Components/ErrorInfoNote/messages.js +32 -6
  25. package/v2Components/ErrorInfoNote/style.scss +278 -6
  26. package/v2Components/FormBuilder/tests/index.test.js +13 -4
  27. package/v2Components/HtmlEditor/HTMLEditor.js +418 -99
  28. package/v2Components/HtmlEditor/__tests__/HTMLEditor.apiErrors.test.js +870 -0
  29. package/v2Components/HtmlEditor/__tests__/HTMLEditor.test.js +1882 -133
  30. package/v2Components/HtmlEditor/__tests__/index.lazy.test.js +27 -16
  31. package/v2Components/HtmlEditor/_htmlEditor.scss +108 -45
  32. package/v2Components/HtmlEditor/_index.lazy.scss +0 -1
  33. package/v2Components/HtmlEditor/components/CodeEditorPane/_codeEditorPane.scss +23 -102
  34. package/v2Components/HtmlEditor/components/CodeEditorPane/index.js +148 -140
  35. package/v2Components/HtmlEditor/components/DeviceToggle/_deviceToggle.scss +2 -1
  36. package/v2Components/HtmlEditor/components/DeviceToggle/index.js +3 -3
  37. package/v2Components/HtmlEditor/components/EditorToolbar/_editorToolbar.scss +9 -1
  38. package/v2Components/HtmlEditor/components/EditorToolbar/index.js +31 -6
  39. package/v2Components/HtmlEditor/components/FullscreenModal/_fullscreenModal.scss +22 -0
  40. package/v2Components/HtmlEditor/components/InAppPreviewPane/DeviceFrame.js +4 -7
  41. package/v2Components/HtmlEditor/components/InAppPreviewPane/__tests__/DeviceFrame.test.js +35 -45
  42. package/v2Components/HtmlEditor/components/InAppPreviewPane/_inAppPreviewPane.scss +1 -3
  43. package/v2Components/HtmlEditor/components/InAppPreviewPane/constants.js +33 -33
  44. package/v2Components/HtmlEditor/components/InAppPreviewPane/index.js +7 -6
  45. package/v2Components/HtmlEditor/components/PreviewPane/_previewPane.scss +7 -10
  46. package/v2Components/HtmlEditor/components/PreviewPane/index.js +22 -43
  47. package/v2Components/HtmlEditor/components/SplitContainer/_splitContainer.scss +1 -1
  48. package/v2Components/HtmlEditor/components/ValidationErrorDisplay/_validationErrorDisplay.scss +18 -0
  49. package/v2Components/HtmlEditor/components/ValidationErrorDisplay/index.js +36 -31
  50. package/v2Components/HtmlEditor/components/ValidationPanel/_validationPanel.scss +46 -34
  51. package/v2Components/HtmlEditor/components/ValidationPanel/constants.js +6 -0
  52. package/v2Components/HtmlEditor/components/ValidationPanel/index.js +52 -46
  53. package/v2Components/HtmlEditor/components/ValidationTabs/_validationTabs.scss +277 -0
  54. package/v2Components/HtmlEditor/components/ValidationTabs/index.js +295 -0
  55. package/v2Components/HtmlEditor/components/ValidationTabs/messages.js +51 -0
  56. package/v2Components/HtmlEditor/constants.js +45 -20
  57. package/v2Components/HtmlEditor/hooks/__tests__/useInAppContent.test.js +373 -16
  58. package/v2Components/HtmlEditor/hooks/__tests__/useValidation.test.js +351 -16
  59. package/v2Components/HtmlEditor/hooks/useEditorContent.js +5 -2
  60. package/v2Components/HtmlEditor/hooks/useInAppContent.js +88 -146
  61. package/v2Components/HtmlEditor/hooks/useValidation.js +213 -56
  62. package/v2Components/HtmlEditor/index.js +1 -1
  63. package/v2Components/HtmlEditor/messages.js +102 -94
  64. package/v2Components/HtmlEditor/utils/__tests__/htmlValidator.enhanced.test.js +214 -45
  65. package/v2Components/HtmlEditor/utils/__tests__/validationAdapter.test.js +134 -0
  66. package/v2Components/HtmlEditor/utils/contentSanitizer.js +40 -41
  67. package/v2Components/HtmlEditor/utils/htmlValidator.js +71 -72
  68. package/v2Components/HtmlEditor/utils/liquidTemplateSupport.js +158 -124
  69. package/v2Components/HtmlEditor/utils/properSyntaxHighlighting.js +23 -25
  70. package/v2Components/HtmlEditor/utils/validationAdapter.js +66 -41
  71. package/v2Components/HtmlEditor/utils/validationConstants.js +38 -0
  72. package/v2Components/MobilePushPreviewV2/constants.js +6 -0
  73. package/v2Components/MobilePushPreviewV2/index.js +33 -7
  74. package/v2Components/TemplatePreview/_templatePreview.scss +55 -24
  75. package/v2Components/TemplatePreview/index.js +47 -32
  76. package/v2Components/TemplatePreview/messages.js +4 -0
  77. package/v2Components/TestAndPreviewSlidebox/_testAndPreviewSlidebox.scss +1 -0
  78. package/v2Containers/BeeEditor/index.js +172 -90
  79. package/v2Containers/BeePopupEditor/_beePopupEditor.scss +14 -0
  80. package/v2Containers/BeePopupEditor/constants.js +10 -0
  81. package/v2Containers/BeePopupEditor/index.js +194 -0
  82. package/v2Containers/BeePopupEditor/tests/index.test.js +627 -0
  83. package/v2Containers/CreativesContainer/SlideBoxContent.js +127 -51
  84. package/v2Containers/CreativesContainer/SlideBoxFooter.js +156 -13
  85. package/v2Containers/CreativesContainer/SlideBoxHeader.js +2 -1
  86. package/v2Containers/CreativesContainer/constants.js +1 -0
  87. package/v2Containers/CreativesContainer/index.js +251 -47
  88. package/v2Containers/CreativesContainer/messages.js +8 -0
  89. package/v2Containers/CreativesContainer/tests/SlideBoxFooter.test.js +11 -2
  90. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +38 -50
  91. package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +103 -0
  92. package/v2Containers/Email/actions.js +7 -0
  93. package/v2Containers/Email/constants.js +5 -1
  94. package/v2Containers/Email/index.js +234 -29
  95. package/v2Containers/Email/messages.js +32 -0
  96. package/v2Containers/Email/reducer.js +12 -1
  97. package/v2Containers/Email/sagas.js +61 -7
  98. package/v2Containers/Email/tests/__snapshots__/reducer.test.js.snap +2 -0
  99. package/v2Containers/Email/tests/reducer.test.js +46 -0
  100. package/v2Containers/Email/tests/sagas.test.js +320 -29
  101. package/v2Containers/EmailWrapper/components/EmailHTMLEditor.js +1246 -0
  102. package/v2Containers/EmailWrapper/components/EmailWrapperView.js +212 -21
  103. package/v2Containers/EmailWrapper/components/HTMLEditorTesting.js +40 -74
  104. package/v2Containers/EmailWrapper/components/__tests__/EmailHTMLEditor.test.js +2614 -0
  105. package/v2Containers/EmailWrapper/components/__tests__/EmailWrapperView.test.js +520 -0
  106. package/v2Containers/EmailWrapper/components/__tests__/HTMLEditorTesting.test.js +2 -67
  107. package/v2Containers/EmailWrapper/constants.js +2 -0
  108. package/v2Containers/EmailWrapper/hooks/useEmailWrapper.js +627 -79
  109. package/v2Containers/EmailWrapper/index.js +103 -23
  110. package/v2Containers/EmailWrapper/messages.js +65 -1
  111. package/v2Containers/EmailWrapper/tests/useEmailWrapper.edgeCases.test.js +955 -0
  112. package/v2Containers/EmailWrapper/tests/useEmailWrapper.test.js +596 -82
  113. package/v2Containers/InApp/__tests__/InAppHTMLEditor.test.js +376 -0
  114. package/v2Containers/InApp/__tests__/sagas.test.js +363 -0
  115. package/v2Containers/InApp/actions.js +7 -0
  116. package/v2Containers/InApp/constants.js +20 -4
  117. package/v2Containers/InApp/index.js +802 -360
  118. package/v2Containers/InApp/index.scss +4 -3
  119. package/v2Containers/InApp/messages.js +7 -3
  120. package/v2Containers/InApp/reducer.js +21 -3
  121. package/v2Containers/InApp/sagas.js +29 -9
  122. package/v2Containers/InApp/selectors.js +25 -5
  123. package/v2Containers/InApp/tests/index.test.js +154 -50
  124. package/v2Containers/InApp/tests/reducer.test.js +34 -0
  125. package/v2Containers/InApp/tests/sagas.test.js +61 -9
  126. package/v2Containers/InApp/tests/selectors.test.js +612 -0
  127. package/v2Containers/InAppWrapper/components/InAppWrapperView.js +151 -0
  128. package/v2Containers/InAppWrapper/components/__tests__/InAppWrapperView.test.js +267 -0
  129. package/v2Containers/InAppWrapper/components/inAppWrapperView.scss +23 -0
  130. package/v2Containers/InAppWrapper/constants.js +16 -0
  131. package/v2Containers/InAppWrapper/hooks/__tests__/useInAppWrapper.test.js +473 -0
  132. package/v2Containers/InAppWrapper/hooks/useInAppWrapper.js +198 -0
  133. package/v2Containers/InAppWrapper/index.js +148 -0
  134. package/v2Containers/InAppWrapper/messages.js +49 -0
  135. package/v2Containers/InappAdvance/index.js +1099 -0
  136. package/v2Containers/InappAdvance/index.scss +10 -0
  137. package/v2Containers/InappAdvance/tests/index.test.js +448 -0
  138. package/v2Containers/Line/Container/ImageCarousel/tests/__snapshots__/content.test.js.snap +3 -0
  139. package/v2Containers/Line/Container/ImageCarousel/tests/__snapshots__/index.test.js.snap +2 -0
  140. package/v2Containers/Line/Container/Wrapper/tests/__snapshots__/index.test.js.snap +2 -0
  141. package/v2Containers/Line/Container/tests/__snapshots__/index.test.js.snap +9 -0
  142. package/v2Containers/MobilePush/Create/index.js +1 -1
  143. package/v2Containers/MobilePush/Edit/index.js +10 -6
  144. package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +12 -0
  145. package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +4 -0
  146. package/v2Containers/TagList/index.js +62 -19
  147. package/v2Containers/Templates/_templates.scss +60 -1
  148. package/v2Containers/Templates/index.js +89 -4
  149. package/v2Containers/Templates/messages.js +4 -0
  150. package/v2Containers/TemplatesV2/TemplatesV2.style.js +4 -2
  151. package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +34 -0
  152. package/v2Components/HtmlEditor/components/ValidationErrorDisplay/__tests__/index.test.js +0 -152
  153. package/v2Containers/EmailWrapper/tests/EmailWrapperView.test.js +0 -214
@@ -0,0 +1,10 @@
1
+ @import '~@capillarytech/cap-ui-library/styles/_variables.scss';
2
+ .cap-inapp-creatives {
3
+ #inapp-layout-dropdown {
4
+ width: 20.5rem;
5
+ }
6
+ .inapp-creative-layout {
7
+ margin-top: $CAP_SPACE_04;
8
+ margin-bottom: $CAP_SPACE_08;
9
+ }
10
+ }
@@ -0,0 +1,448 @@
1
+ import React from 'react';
2
+ import { Provider } from 'react-redux';
3
+ import '@testing-library/jest-dom';
4
+ import { injectIntl } from 'react-intl';
5
+ import { configureStore } from '@capillarytech/vulcan-react-sdk/utils';
6
+ import history from '../../../utils/history';
7
+ import { initialReducer } from '../../../initialReducer';
8
+ import { InappAdvanced } from '../index';
9
+ import { render, screen, fireEvent, waitFor } from '../../../utils/test-utils';
10
+
11
+ // Mock child components that are complex or not under test
12
+ jest.mock('../../BeePopupEditor', () => ({
13
+ __esModule: true,
14
+ default: (props) => (
15
+ <div
16
+ data-testid={`bee-popup-editor-${props.device}`}
17
+ data-device={props.device}
18
+ data-template-layout-type={props.templateLayoutType}
19
+ >
20
+ BeePopupEditor - {props.device}
21
+ <button
22
+ onClick={() => props.saveBeeData('{"test": "json"}', '<p>Test HTML</p>', props.device)}
23
+ data-testid={`save-bee-data-${props.device}`}
24
+ >
25
+ Save Bee Data
26
+ </button>
27
+ <button
28
+ onClick={() => props.saveBeeInstance({ mockInstance: true })}
29
+ data-testid={`save-bee-instance-${props.device}`}
30
+ >
31
+ Save Bee Instance
32
+ </button>
33
+ </div>
34
+ ),
35
+ }));
36
+
37
+ let store;
38
+ beforeAll(() => {
39
+ store = configureStore({}, initialReducer, history);
40
+ });
41
+
42
+ const ComponentToRender = injectIntl(InappAdvanced);
43
+ const renderComponent = (props) =>
44
+ render(
45
+ <Provider store={store}>
46
+ <ComponentToRender {...props} />
47
+ </Provider>
48
+ );
49
+
50
+ describe('InappAdvanced Component', () => {
51
+ let defaultProps;
52
+ let mockActions;
53
+
54
+ beforeEach(() => {
55
+ mockActions = {
56
+ getBeePopupBuilderToken: jest.fn(),
57
+ createInAppTemplate: jest.fn(),
58
+ editTemplate: jest.fn(),
59
+ clearCreateResponse: jest.fn(),
60
+ };
61
+
62
+ const mockGlobalActions = {
63
+ fetchSchemaForEntity: jest.fn(),
64
+ };
65
+
66
+ defaultProps = {
67
+ intl: {
68
+ formatMessage: jest.fn((msg) => msg.defaultMessage || msg.id),
69
+ },
70
+ actions: mockActions,
71
+ globalActions: mockGlobalActions,
72
+ isFullMode: true,
73
+ onCreateComplete: jest.fn(),
74
+ params: { id: 'test-id' },
75
+ templateData: {},
76
+ editData: {},
77
+ accountData: {
78
+ selectedWeChatAccount: {
79
+ sourceAccountIdentifier: 'test-account',
80
+ id: 'account-123',
81
+ configs: {
82
+ android: '1',
83
+ ios: '1',
84
+ },
85
+ },
86
+ },
87
+ location: {
88
+ pathname: '/inapp/create',
89
+ query: { type: 'inapp' },
90
+ search: '',
91
+ },
92
+ getDefaultTags: 'inapp',
93
+ supportedTags: [],
94
+ metaEntities: {
95
+ tags: {
96
+ standard: [],
97
+ },
98
+ },
99
+ injectedTags: [],
100
+ getFormData: jest.fn(),
101
+ templateName: 'Test Template',
102
+ setTemplateName: jest.fn(),
103
+ beePopupBuilderTokenFetching: false,
104
+ beePopupBuilderToken: {
105
+ uuid: 'test-uuid-123',
106
+ token: 'test-token-456',
107
+ },
108
+ };
109
+ });
110
+
111
+ describe('Component Initialization', () => {
112
+ it('should render without crashing', () => {
113
+ renderComponent(defaultProps);
114
+
115
+ expect(screen.getByText('Create')).toBeInTheDocument();
116
+ });
117
+
118
+ it('should call getBeePopupBuilderToken on mount', () => {
119
+ renderComponent(defaultProps);
120
+
121
+ expect(mockActions.getBeePopupBuilderToken).toHaveBeenCalledTimes(1);
122
+ });
123
+
124
+ it('should show loading spinner when beePopupBuilderTokenFetching is true', () => {
125
+ renderComponent({
126
+ ...defaultProps,
127
+ beePopupBuilderTokenFetching: true,
128
+ });
129
+
130
+ // The spinner should be present but we can't easily test for it without mocking
131
+ expect(screen.getByText('Create')).toBeInTheDocument();
132
+ });
133
+ });
134
+
135
+ describe('Layout Selection', () => {
136
+ it('should render layout selection when isFullMode is true', () => {
137
+ renderComponent({
138
+ ...defaultProps,
139
+ isFullMode: true,
140
+ });
141
+
142
+ expect(screen.getByText('Creative layout')).toBeInTheDocument();
143
+ // CapSelect from cap-ui-library should render a select element
144
+ const selectElements = screen.getAllByRole('button');
145
+ expect(selectElements.length).toBeGreaterThan(0);
146
+ });
147
+
148
+ it('should update layout type when selection changes', () => {
149
+ renderComponent(defaultProps);
150
+
151
+ // Layout selection should be rendered
152
+ expect(screen.getByText('Creative layout')).toBeInTheDocument();
153
+ });
154
+ });
155
+
156
+ describe('Device Tabs', () => {
157
+ it('should render device tabs with Android and iOS options', () => {
158
+ renderComponent(defaultProps);
159
+
160
+ expect(screen.getByText('Android')).toBeInTheDocument();
161
+ expect(screen.getByText('IOS')).toBeInTheDocument();
162
+ });
163
+
164
+ it('should show BeePopupEditor for Android when Android tab is active', () => {
165
+ renderComponent({
166
+ ...defaultProps,
167
+ beePopupBuilderToken: { uuid: 'test-uuid' },
168
+ });
169
+
170
+ expect(screen.getByTestId('bee-popup-editor-ANDROID')).toBeInTheDocument();
171
+ });
172
+
173
+ it('should only show supported device tabs', () => {
174
+ renderComponent({
175
+ ...defaultProps,
176
+ accountData: {
177
+ selectedWeChatAccount: {
178
+ configs: {
179
+ android: '0', // Not supported
180
+ ios: '1', // Supported
181
+ },
182
+ },
183
+ },
184
+ });
185
+
186
+ expect(screen.queryByText('Android')).not.toBeInTheDocument();
187
+ expect(screen.getByText('IOS')).toBeInTheDocument();
188
+ });
189
+ });
190
+
191
+ describe('BeePopupEditor Integration', () => {
192
+ it('should pass correct props to BeePopupEditor', () => {
193
+ renderComponent({
194
+ ...defaultProps,
195
+ beePopupBuilderToken: { uuid: 'test-uuid' },
196
+ });
197
+
198
+ const beeEditor = screen.getByTestId('bee-popup-editor-ANDROID');
199
+ expect(beeEditor).toHaveAttribute('data-device', 'ANDROID');
200
+ expect(beeEditor).toHaveAttribute('data-template-layout-type', 'MODAL');
201
+ });
202
+
203
+ it('should handle saveBeeData callback correctly', () => {
204
+ renderComponent({
205
+ ...defaultProps,
206
+ beePopupBuilderToken: { uuid: 'test-uuid' },
207
+ });
208
+
209
+ const saveBeeDataButton = screen.getByTestId('save-bee-data-ANDROID');
210
+ fireEvent.click(saveBeeDataButton);
211
+
212
+ // The component should update its internal state with the JSON and HTML
213
+ expect(saveBeeDataButton).toBeInTheDocument(); // Basic check that callback was handled
214
+ });
215
+
216
+ it('should handle saveBeeInstance callback correctly', () => {
217
+ renderComponent({
218
+ ...defaultProps,
219
+ beePopupBuilderToken: { uuid: 'test-uuid' },
220
+ });
221
+
222
+ const saveBeeInstanceButton = screen.getByTestId('save-bee-instance-ANDROID');
223
+ fireEvent.click(saveBeeInstanceButton);
224
+
225
+ // The component should store the bee instance
226
+ expect(saveBeeInstanceButton).toBeInTheDocument(); // Basic check that callback was handled
227
+ });
228
+
229
+ it('should not render BeePopupEditor when no token is available', () => {
230
+ renderComponent({
231
+ ...defaultProps,
232
+ beePopupBuilderToken: null,
233
+ });
234
+
235
+ expect(screen.queryByTestId('bee-popup-editor-ANDROID')).not.toBeInTheDocument();
236
+ });
237
+ });
238
+
239
+ describe('Action Buttons', () => {
240
+ it('should render Create button in full mode for new templates', () => {
241
+ renderComponent({
242
+ ...defaultProps,
243
+ isFullMode: true,
244
+ });
245
+
246
+ expect(screen.getByText('Create')).toBeInTheDocument();
247
+ });
248
+
249
+ it('should render Update button in edit mode', () => {
250
+ renderComponent({
251
+ ...defaultProps,
252
+ isFullMode: true,
253
+ editData: {
254
+ templateDetails: {
255
+ name: 'Existing Template',
256
+ versions: {
257
+ base: {
258
+ content: {
259
+ ANDROID: { title: 'Test' },
260
+ },
261
+ },
262
+ },
263
+ },
264
+ },
265
+ });
266
+
267
+ expect(screen.getByText('Update')).toBeInTheDocument();
268
+ });
269
+
270
+ it('should render Done button in library mode', () => {
271
+ renderComponent({
272
+ ...defaultProps,
273
+ isFullMode: false,
274
+ });
275
+
276
+ expect(screen.getByText('Done')).toBeInTheDocument();
277
+ });
278
+
279
+ it('should call appropriate action when Create button is clicked', async () => {
280
+ renderComponent({
281
+ ...defaultProps,
282
+ isFullMode: true,
283
+ });
284
+
285
+ const createButton = screen.getByText('Create');
286
+ fireEvent.click(createButton);
287
+
288
+ // Wait for async operations to complete
289
+ await waitFor(() => {
290
+ expect(mockActions.createInAppTemplate).toHaveBeenCalled();
291
+ }, { timeout: 3000 });
292
+ });
293
+ });
294
+
295
+ describe('Form Data Integration', () => {
296
+ it('should call getFormData when in library mode', async () => {
297
+ const mockGetFormData = jest.fn();
298
+ renderComponent({
299
+ ...defaultProps,
300
+ isFullMode: false,
301
+ getFormData: mockGetFormData,
302
+ });
303
+
304
+ const doneButton = screen.getByText('Done');
305
+ fireEvent.click(doneButton);
306
+
307
+ // Wait for async operations to complete
308
+ await waitFor(() => {
309
+ expect(mockGetFormData).toHaveBeenCalledWith({
310
+ value: expect.any(Object),
311
+ _id: 'test-id',
312
+ validity: true,
313
+ type: 'INAPP',
314
+ });
315
+ }, { timeout: 3000 });
316
+ });
317
+ });
318
+
319
+ describe('Template Name Integration', () => {
320
+ it('should display the template name when provided', () => {
321
+ renderComponent({
322
+ ...defaultProps,
323
+ templateName: 'My Awesome Template',
324
+ });
325
+
326
+ // Template name should be used in the payload creation
327
+ expect(screen.getByText('Create')).toBeInTheDocument();
328
+ });
329
+
330
+ it('should handle setTemplateName callback', () => {
331
+ const mockSetTemplateName = jest.fn();
332
+ renderComponent({
333
+ ...defaultProps,
334
+ setTemplateName: mockSetTemplateName,
335
+ });
336
+
337
+ // The component should be able to update template name
338
+ expect(mockSetTemplateName).toBeDefined();
339
+ });
340
+ });
341
+
342
+ describe('Account Data Integration', () => {
343
+ it('should handle missing account data gracefully', () => {
344
+ renderComponent({
345
+ ...defaultProps,
346
+ accountData: {},
347
+ });
348
+
349
+ // Component should render without crashing
350
+ expect(screen.getByText('Create')).toBeInTheDocument();
351
+ });
352
+
353
+ it('should use account data in payload creation', () => {
354
+ renderComponent({
355
+ ...defaultProps,
356
+ accountData: {
357
+ selectedWeChatAccount: {
358
+ sourceAccountIdentifier: 'test-account-123',
359
+ id: 'account-456',
360
+ },
361
+ },
362
+ });
363
+
364
+ // Account data should be used when creating the payload
365
+ expect(screen.getByText('Create')).toBeInTheDocument();
366
+ });
367
+ });
368
+
369
+ describe('Edit Flow', () => {
370
+ it('should initialize edit flow when editData is provided', () => {
371
+ renderComponent({
372
+ ...defaultProps,
373
+ editData: {
374
+ templateDetails: {
375
+ name: 'Existing Template',
376
+ createdAt: '2023-01-01',
377
+ versions: {
378
+ base: {
379
+ content: {
380
+ ANDROID: {
381
+ isBEEeditor: true,
382
+ beeJson: '{"test": "json"}',
383
+ beeHtml: '<p>Test HTML</p>',
384
+ },
385
+ IOS: {
386
+ isBEEeditor: true,
387
+ beeJson: '{"test": "ios-json"}',
388
+ beeHtml: '<p>iOS Test HTML</p>',
389
+ },
390
+ },
391
+ },
392
+ },
393
+ },
394
+ },
395
+ });
396
+
397
+ // Should show Update button in edit flow
398
+ expect(screen.getByText('Update')).toBeInTheDocument();
399
+ });
400
+ });
401
+
402
+ describe('Error Handling', () => {
403
+ it('should handle missing props gracefully', () => {
404
+ expect(() => {
405
+ renderComponent({
406
+ intl: {
407
+ formatMessage: jest.fn((msg) => msg.defaultMessage || msg.id),
408
+ },
409
+ actions: mockActions,
410
+ globalActions: {
411
+ fetchSchemaForEntity: jest.fn(),
412
+ },
413
+ location: {
414
+ pathname: '/inapp/create',
415
+ query: { type: 'inapp' },
416
+ search: '',
417
+ },
418
+ });
419
+ }).not.toThrow();
420
+ });
421
+
422
+ it('should handle undefined beePopupBuilderToken gracefully', () => {
423
+ renderComponent({
424
+ ...defaultProps,
425
+ beePopupBuilderToken: undefined,
426
+ });
427
+
428
+ expect(screen.getByText('Create')).toBeInTheDocument();
429
+ });
430
+ });
431
+
432
+ describe('Responsive Behavior', () => {
433
+ it('should apply correct CSS classes for different modes', () => {
434
+ const { container } = renderComponent(defaultProps);
435
+
436
+ expect(container.querySelector('.cap-inapp-creatives')).toBeInTheDocument();
437
+ });
438
+
439
+ it('should show footer with correct classes in library mode', () => {
440
+ const { container } = renderComponent({
441
+ ...defaultProps,
442
+ isFullMode: false,
443
+ });
444
+
445
+ expect(container.querySelector('.inapp-footer-lib')).toBeInTheDocument();
446
+ });
447
+ });
448
+ });
@@ -2281,6 +2281,7 @@ new message content.",
2281
2281
  "email": [Function],
2282
2282
  "facebookPreview": [Function],
2283
2283
  "gallery": [Function],
2284
+ "inApp": [Function],
2284
2285
  "language": [Function],
2285
2286
  "navigationConfig": [Function],
2286
2287
  "previewAndTest": [Function],
@@ -5964,6 +5965,7 @@ new message content.",
5964
5965
  "email": [Function],
5965
5966
  "facebookPreview": [Function],
5966
5967
  "gallery": [Function],
5968
+ "inApp": [Function],
5967
5969
  "language": [Function],
5968
5970
  "navigationConfig": [Function],
5969
5971
  "previewAndTest": [Function],
@@ -9786,6 +9788,7 @@ new message content.",
9786
9788
  "email": [Function],
9787
9789
  "facebookPreview": [Function],
9788
9790
  "gallery": [Function],
9791
+ "inApp": [Function],
9789
9792
  "language": [Function],
9790
9793
  "navigationConfig": [Function],
9791
9794
  "previewAndTest": [Function],
@@ -2281,6 +2281,7 @@ new message content.",
2281
2281
  "email": [Function],
2282
2282
  "facebookPreview": [Function],
2283
2283
  "gallery": [Function],
2284
+ "inApp": [Function],
2284
2285
  "language": [Function],
2285
2286
  "navigationConfig": [Function],
2286
2287
  "previewAndTest": [Function],
@@ -21554,6 +21555,7 @@ new message content.",
21554
21555
  "email": [Function],
21555
21556
  "facebookPreview": [Function],
21556
21557
  "gallery": [Function],
21558
+ "inApp": [Function],
21557
21559
  "language": [Function],
21558
21560
  "navigationConfig": [Function],
21559
21561
  "previewAndTest": [Function],
@@ -2281,6 +2281,7 @@ new message content.",
2281
2281
  "email": [Function],
2282
2282
  "facebookPreview": [Function],
2283
2283
  "gallery": [Function],
2284
+ "inApp": [Function],
2284
2285
  "language": [Function],
2285
2286
  "navigationConfig": [Function],
2286
2287
  "previewAndTest": [Function],
@@ -34885,6 +34886,7 @@ new message content.",
34885
34886
  "email": [Function],
34886
34887
  "facebookPreview": [Function],
34887
34888
  "gallery": [Function],
34889
+ "inApp": [Function],
34888
34890
  "language": [Function],
34889
34891
  "navigationConfig": [Function],
34890
34892
  "previewAndTest": [Function],
@@ -2281,6 +2281,7 @@ new message content.",
2281
2281
  "email": [Function],
2282
2282
  "facebookPreview": [Function],
2283
2283
  "gallery": [Function],
2284
+ "inApp": [Function],
2284
2285
  "language": [Function],
2285
2286
  "navigationConfig": [Function],
2286
2287
  "previewAndTest": [Function],
@@ -11299,6 +11300,7 @@ new message content.",
11299
11300
  "email": [Function],
11300
11301
  "facebookPreview": [Function],
11301
11302
  "gallery": [Function],
11303
+ "inApp": [Function],
11302
11304
  "language": [Function],
11303
11305
  "navigationConfig": [Function],
11304
11306
  "previewAndTest": [Function],
@@ -20501,6 +20503,7 @@ new message content.",
20501
20503
  "email": [Function],
20502
20504
  "facebookPreview": [Function],
20503
20505
  "gallery": [Function],
20506
+ "inApp": [Function],
20504
20507
  "language": [Function],
20505
20508
  "navigationConfig": [Function],
20506
20509
  "previewAndTest": [Function],
@@ -29980,6 +29983,7 @@ new message content.",
29980
29983
  "email": [Function],
29981
29984
  "facebookPreview": [Function],
29982
29985
  "gallery": [Function],
29986
+ "inApp": [Function],
29983
29987
  "language": [Function],
29984
29988
  "navigationConfig": [Function],
29985
29989
  "previewAndTest": [Function],
@@ -41680,6 +41684,7 @@ new message content.",
41680
41684
  "email": [Function],
41681
41685
  "facebookPreview": [Function],
41682
41686
  "gallery": [Function],
41687
+ "inApp": [Function],
41683
41688
  "language": [Function],
41684
41689
  "navigationConfig": [Function],
41685
41690
  "previewAndTest": [Function],
@@ -53422,6 +53427,7 @@ new message content.",
53422
53427
  "email": [Function],
53423
53428
  "facebookPreview": [Function],
53424
53429
  "gallery": [Function],
53430
+ "inApp": [Function],
53425
53431
  "language": [Function],
53426
53432
  "navigationConfig": [Function],
53427
53433
  "previewAndTest": [Function],
@@ -61214,6 +61220,7 @@ new message content.",
61214
61220
  "email": [Function],
61215
61221
  "facebookPreview": [Function],
61216
61222
  "gallery": [Function],
61223
+ "inApp": [Function],
61217
61224
  "language": [Function],
61218
61225
  "navigationConfig": [Function],
61219
61226
  "previewAndTest": [Function],
@@ -69424,6 +69431,7 @@ new message content.",
69424
69431
  "email": [Function],
69425
69432
  "facebookPreview": [Function],
69426
69433
  "gallery": [Function],
69434
+ "inApp": [Function],
69427
69435
  "language": [Function],
69428
69436
  "navigationConfig": [Function],
69429
69437
  "previewAndTest": [Function],
@@ -77911,6 +77919,7 @@ new message content.",
77911
77919
  "email": [Function],
77912
77920
  "facebookPreview": [Function],
77913
77921
  "gallery": [Function],
77922
+ "inApp": [Function],
77914
77923
  "language": [Function],
77915
77924
  "navigationConfig": [Function],
77916
77925
  "previewAndTest": [Function],
@@ -114,7 +114,7 @@ export class Create extends React.Component { // eslint-disable-line react/prefe
114
114
  if (nextProps.iosCtasData && isEmpty(this.state.templateCta)) {
115
115
  this.setState({showIosCtaTable: true});
116
116
  }
117
- if (nextProps.metaEntities && nextProps.metaEntities.layouts && nextProps.metaEntities.layouts.length > 0 && isEmpty(this.state.schema)) {
117
+ if (nextProps.metaEntities && nextProps.metaEntities.layouts && nextProps.metaEntities.layouts.length > 0 && isEmpty(this.state.schema) && isEmpty(this.state.formData)) {
118
118
  const schema = this.props.params.mode === "text" ? nextProps.metaEntities.layouts[0].definition.textSchema : nextProps.metaEntities.layouts[0].definition.imageSchema;
119
119
  const isAndroidSupported = get(this, "props.Templates.selectedWeChatAccount.configs.android") === '1';
120
120
  const isIosSupported = get(this, "props.Templates.selectedWeChatAccount.configs.ios") === '1';
@@ -167,8 +167,8 @@ export class Edit extends React.Component { // eslint-disable-line react/prefer-
167
167
  };
168
168
  this.props.actions.getMobilepushTemplatesList('mobilepush', params);
169
169
  }
170
- if (nextProps.metaEntities && nextProps.metaEntities.layouts && nextProps.metaEntities.layouts.length > 0 && _.isEmpty(this.state.fullSchema)) {
171
- this.setState({fullSchema: nextProps.metaEntities.layouts[0].definition, schema: (nextProps.location.query.module === 'loyalty') ? nextProps.metaEntities.layouts[0].definition.textSchema : {}}, () => {
170
+ if (nextProps.metaEntities && nextProps.metaEntities.layouts && nextProps.metaEntities.layouts.length > 0 && _.isEmpty(this.state.fullSchema) && _.isEmpty(this.state.formData)) {
171
+ this.setState({ fullSchema: nextProps.metaEntities.layouts[0].definition, schema: (nextProps.location.query.module === 'loyalty') ? nextProps.metaEntities.layouts[0].definition.textSchema : {} }, () => {
172
172
  this.handleEditSchemaOnPropsChange(nextProps, selectedWeChatAccount);
173
173
  const templateId = get(this, "props.params.id");
174
174
  if (nextProps.location.query.module !== 'loyalty' && templateId && templateId !== 'temp') {
@@ -388,11 +388,11 @@ export class Edit extends React.Component { // eslint-disable-line react/prefer-
388
388
  }
389
389
  });
390
390
  } else {
391
- selectedAccount = accounts[0];
391
+ selectedAccount = accounts?.[0];
392
392
  formData['mobilepush-accounts'] = selectedAccount?.id;
393
393
  }
394
394
  this.props.actions.setWeChatAccount(selectedAccount);
395
- this.setState({formData, accountsOptions: accounts.map((acc) => ({key: acc.id, label: acc.name, value: acc.id}))});
395
+ this.setState({ formData, accountsOptions: accounts?.map((acc) => ({ key: acc.id, label: acc.name, value: acc.id })) });
396
396
  };
397
397
 
398
398
  createDefinition = (account) => ({
@@ -2018,12 +2018,16 @@ export class Edit extends React.Component { // eslint-disable-line react/prefer-
2018
2018
  if (this.props.supportedTags) {
2019
2019
  tags = this.props.supportedTags;
2020
2020
  }
2021
+ if (this.props.supportedTags) {
2022
+ tags = this.props.supportedTags;
2023
+ }
2021
2024
  return (
2022
2025
  <div className="creatives-mobilepush-edit mobilepush-wrapper">
2023
2026
  <CapSpin spinning={spinning}>
2024
2027
  <CapRow>
2025
2028
  <CapColumn>
2026
- {!this.props.isLoadingMetaEntities && <FormBuilder
2029
+ <FormBuilder
2030
+ key={!_.isEmpty(schema) ? 'has-schema' : 'no-schema'}
2027
2031
  channel={MOBILE_PUSH}
2028
2032
  schema={schema}
2029
2033
  showLiquidErrorInFooter={this.props.showLiquidErrorInFooter}
@@ -2058,7 +2062,7 @@ export class Edit extends React.Component { // eslint-disable-line react/prefer-
2058
2062
  hideTestAndPreviewBtn={this.props.hideTestAndPreviewBtn}
2059
2063
  isFullMode={this.props.isFullMode}
2060
2064
  eventContextTags={this.props?.eventContextTags}
2061
- />}
2065
+ />
2062
2066
  </CapColumn>
2063
2067
  {this.props.iosCtasData && this.state.showIosCtaTable &&
2064
2068
  <CapSlideBox