@capillarytech/creatives-library 8.0.87-alpha.21 → 8.0.87-alpha.23
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/Templates/constants.js +6 -0
- package/containers/Templates/index.js +44 -24
- package/package.json +1 -1
- package/services/api.js +22 -12
- package/services/tests/api.test.js +5 -1
- package/utils/commonUtils.js +64 -10
- package/utils/tests/commonUtil.test.js +108 -3
- package/utils/tests/transformerUtils.test.js +2127 -0
- package/utils/transformerUtils.js +42 -96
- package/v2Components/CapImageUpload/index.js +13 -10
- package/v2Components/CapVideoUpload/index.js +12 -9
- package/v2Components/CapWhatsappCTA/messages.js +4 -0
- package/v2Components/CapWhatsappCarouselButton/constant.js +56 -0
- package/v2Components/CapWhatsappCarouselButton/index.js +446 -0
- package/v2Components/CapWhatsappCarouselButton/index.scss +39 -0
- package/v2Components/CapWhatsappCarouselButton/tests/index.test.js +237 -0
- package/v2Components/FormBuilder/constants.js +8 -0
- package/v2Components/FormBuilder/index.js +2 -2
- package/v2Components/TemplatePreview/_templatePreview.scss +20 -0
- package/v2Components/TemplatePreview/assets/images/empty_image_preview.svg +4 -0
- package/v2Components/TemplatePreview/assets/images/empty_video_preview.svg +4 -0
- package/v2Components/TemplatePreview/index.js +160 -105
- package/v2Components/TemplatePreview/tests/__snapshots__/index.test.js.snap +6 -6
- package/v2Containers/Cap/tests/saga.test.js +90 -1
- package/v2Containers/CreativesContainer/SlideBoxContent.js +0 -6
- package/v2Containers/CreativesContainer/constants.js +8 -1
- package/v2Containers/CreativesContainer/index.js +102 -9
- package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +3 -0
- package/v2Containers/Email/index.js +0 -1
- package/v2Containers/EmailWrapper/components/EmailWrapperView.js +192 -0
- package/v2Containers/EmailWrapper/constants.js +11 -1
- package/v2Containers/EmailWrapper/hooks/useEmailWrapper.js +343 -0
- package/v2Containers/EmailWrapper/index.js +116 -300
- package/v2Containers/EmailWrapper/mockdata/mockdata.js +119 -0
- package/v2Containers/EmailWrapper/tests/EmailWrapperView.test.js +214 -0
- package/v2Containers/EmailWrapper/tests/index.test.js +101 -0
- package/v2Containers/EmailWrapper/tests/useEmailWrapper.test.js +601 -0
- package/v2Containers/MobilePush/Edit/index.js +0 -1
- package/v2Containers/MobilepushWrapper/index.js +1 -2
- package/v2Containers/Sms/Create/index.js +0 -1
- package/v2Containers/SmsWrapper/index.js +0 -2
- package/v2Containers/Templates/_templates.scss +47 -0
- package/v2Containers/Templates/index.js +55 -5
- package/v2Containers/Templates/tests/__snapshots__/index.test.js.snap +177 -156
- package/v2Containers/TemplatesV2/index.js +2 -2
- package/v2Containers/Whatsapp/constants.js +87 -1
- package/v2Containers/Whatsapp/index.js +715 -190
- package/v2Containers/Whatsapp/index.scss +52 -1
- package/v2Containers/Whatsapp/messages.js +38 -2
- package/v2Containers/Whatsapp/styles.scss +5 -0
- package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +27722 -90751
- package/v2Containers/Whatsapp/tests/__snapshots__/utils.test.js.snap +6 -0
- package/v2Containers/Whatsapp/tests/mockData.js +3 -7
- package/v2Containers/Whatsapp/tests/utils.test.js +178 -1
- package/v2Containers/Whatsapp/utils.js +52 -0
- package/v2Containers/Zalo/index.js +47 -15
- package/v2Containers/Zalo/index.scss +8 -0
- package/v2Containers/Zalo/messages.js +4 -0
- package/v2Containers/mockdata.js +2 -0
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render, fireEvent, within, screen } from '@testing-library/react';
|
|
3
|
+
import '@testing-library/jest-dom';
|
|
4
|
+
import { IntlProvider } from 'react-intl';
|
|
5
|
+
import { Provider } from 'react-redux';
|
|
6
|
+
import { configureStore } from '@capillarytech/vulcan-react-sdk/utils'
|
|
7
|
+
import { initialReducer } from '../../../initialReducer';
|
|
8
|
+
import EmailWrapperView from '../components/EmailWrapperView';
|
|
9
|
+
import { EmailWrapperViewMockProps } from '../mockdata/mockdata';
|
|
10
|
+
import { EMAIL_CREATE_MODES } from '../constants';
|
|
11
|
+
import history from '../../../utils/history';
|
|
12
|
+
|
|
13
|
+
// This mock needs to be before any imports
|
|
14
|
+
jest.mock('../../../v2Containers/Email', () => ({
|
|
15
|
+
__esModule: true,
|
|
16
|
+
default: () => <div data-testid="email-create-container" />,
|
|
17
|
+
}));
|
|
18
|
+
|
|
19
|
+
// Mock react-router
|
|
20
|
+
jest.mock('react-router-dom', () => ({
|
|
21
|
+
...jest.requireActual('react-router-dom'),
|
|
22
|
+
useHistory: () => ({
|
|
23
|
+
push: jest.fn(),
|
|
24
|
+
replace: jest.fn(),
|
|
25
|
+
go: jest.fn(),
|
|
26
|
+
goBack: jest.fn(),
|
|
27
|
+
goForward: jest.fn(),
|
|
28
|
+
location: { search: '' }
|
|
29
|
+
}),
|
|
30
|
+
}));
|
|
31
|
+
|
|
32
|
+
// Mock redux-auth-wrapper
|
|
33
|
+
jest.mock('redux-auth-wrapper/history4/redirect', () => ({
|
|
34
|
+
connectedRouterRedirect: () => (Component) => Component,
|
|
35
|
+
}));
|
|
36
|
+
|
|
37
|
+
// Mock react-router
|
|
38
|
+
jest.mock('connected-react-router', () => ({
|
|
39
|
+
push: jest.fn(),
|
|
40
|
+
replace: jest.fn(),
|
|
41
|
+
go: jest.fn(),
|
|
42
|
+
goBack: jest.fn(),
|
|
43
|
+
goForward: jest.fn(),
|
|
44
|
+
}));
|
|
45
|
+
|
|
46
|
+
// Mock the saga injector
|
|
47
|
+
jest.mock('@capillarytech/vulcan-react-sdk/utils/injectSaga', () => ({
|
|
48
|
+
__esModule: true,
|
|
49
|
+
default: () => (Component) => Component,
|
|
50
|
+
}));
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
const initialState = {
|
|
54
|
+
cap: { loading: false, error: null },
|
|
55
|
+
router: { location: { pathname: '/' } }
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
describe('EmailWrapperView', () => {
|
|
59
|
+
let store;
|
|
60
|
+
|
|
61
|
+
beforeAll(() => {
|
|
62
|
+
store = configureStore(initialState, initialReducer, history);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
const renderWithProviders = (component) => {
|
|
66
|
+
return render(
|
|
67
|
+
<Provider store={store}>
|
|
68
|
+
<IntlProvider locale="en" messages={{}}>
|
|
69
|
+
{component}
|
|
70
|
+
</IntlProvider>
|
|
71
|
+
</Provider>
|
|
72
|
+
);
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
it('renders mode selection UI when step is modeSelection', () => {
|
|
76
|
+
const props = {
|
|
77
|
+
...EmailWrapperViewMockProps,
|
|
78
|
+
step: 'modeSelection',
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const { getByText } = renderWithProviders(<EmailWrapperView {...props} />);
|
|
82
|
+
expect(getByText('Create using editor')).toBeInTheDocument();
|
|
83
|
+
expect(getByText('Upload zip file')).toBeInTheDocument();
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('shows template name input in full mode', () => {
|
|
87
|
+
const props = {
|
|
88
|
+
...EmailWrapperViewMockProps,
|
|
89
|
+
isFullMode: true,
|
|
90
|
+
step: 'modeSelection',
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
const { container } = renderWithProviders(<EmailWrapperView {...props} />);
|
|
94
|
+
const input = container.querySelector('.ant-input');
|
|
95
|
+
expect(input).toBeInTheDocument();
|
|
96
|
+
expect(input.value).toBe(props.templateName);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('handles template name changes', () => {
|
|
100
|
+
const props = {
|
|
101
|
+
...EmailWrapperViewMockProps,
|
|
102
|
+
isFullMode: true,
|
|
103
|
+
step: 'modeSelection',
|
|
104
|
+
onTemplateNameChange: jest.fn(),
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
const { container } = renderWithProviders(<EmailWrapperView {...props} />);
|
|
108
|
+
const input = container.querySelector('.ant-input');
|
|
109
|
+
expect(input).toBeInTheDocument();
|
|
110
|
+
|
|
111
|
+
// Create a change event and fire it
|
|
112
|
+
fireEvent.change(input, { target: { value: 'New Template Name' } });
|
|
113
|
+
|
|
114
|
+
// Just verify that the function was called
|
|
115
|
+
expect(props.onTemplateNameChange).toHaveBeenCalled();
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it('shows upload button when in upload mode', () => {
|
|
119
|
+
const props = {
|
|
120
|
+
...EmailWrapperViewMockProps,
|
|
121
|
+
step: 'modeSelection',
|
|
122
|
+
emailCreateMode: EMAIL_CREATE_MODES.UPLOAD,
|
|
123
|
+
uploadButtonLabel: 'Upload',
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
const { getByText, container } = renderWithProviders(<EmailWrapperView {...props} />);
|
|
127
|
+
|
|
128
|
+
// Find the "Upload zip file" section
|
|
129
|
+
const modeSelectionSection = getByText('Upload zip file').closest('div');
|
|
130
|
+
|
|
131
|
+
// Find any button in the upload section
|
|
132
|
+
const uploadButton = container.querySelector('.ant-btn');
|
|
133
|
+
|
|
134
|
+
// Verify that we found an upload button
|
|
135
|
+
expect(uploadButton).toBeInTheDocument();
|
|
136
|
+
expect(uploadButton.textContent).toContain('Upload');
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it('shows loading spinner when uploading', () => {
|
|
140
|
+
const props = {
|
|
141
|
+
...EmailWrapperViewMockProps,
|
|
142
|
+
isUploading: true,
|
|
143
|
+
emailCreateMode: EMAIL_CREATE_MODES.UPLOAD,
|
|
144
|
+
step: 'modeSelection',
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
const { container } = renderWithProviders(<EmailWrapperView {...props} />);
|
|
148
|
+
const spinner = container.querySelector('.ant-spin');
|
|
149
|
+
expect(spinner).toBeInTheDocument();
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
it('shows content creation UI when not in mode selection', () => {
|
|
153
|
+
const props = {
|
|
154
|
+
...EmailWrapperViewMockProps,
|
|
155
|
+
step: 'contentCreation',
|
|
156
|
+
isShowEmailCreate: true,
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
const { getByTestId } = renderWithProviders(<EmailWrapperView {...props} />);
|
|
160
|
+
expect(getByTestId('email-create-container')).toBeInTheDocument();
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it('disables upload button when template name is empty in full mode', () => {
|
|
164
|
+
const props = {
|
|
165
|
+
...EmailWrapperViewMockProps,
|
|
166
|
+
isFullMode: true,
|
|
167
|
+
step: 'modeSelection',
|
|
168
|
+
emailCreateMode: EMAIL_CREATE_MODES.UPLOAD,
|
|
169
|
+
templateName: '',
|
|
170
|
+
isTemplateNameEmpty: true,
|
|
171
|
+
uploadButtonLabel: 'Upload',
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
const { getByText, container } = renderWithProviders(<EmailWrapperView {...props} />);
|
|
175
|
+
|
|
176
|
+
// Find the Upload section
|
|
177
|
+
getByText('Upload zip file');
|
|
178
|
+
|
|
179
|
+
// Find button in the container
|
|
180
|
+
const uploadButton = container.querySelector('.ant-btn');
|
|
181
|
+
|
|
182
|
+
// Check if the button is disabled
|
|
183
|
+
expect(uploadButton).toBeDisabled();
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
it('shows error message when template name is empty during upload', () => {
|
|
187
|
+
const props = {
|
|
188
|
+
...EmailWrapperViewMockProps,
|
|
189
|
+
isFullMode: true,
|
|
190
|
+
step: 'modeSelection',
|
|
191
|
+
emailCreateMode: EMAIL_CREATE_MODES.UPLOAD,
|
|
192
|
+
templateName: '',
|
|
193
|
+
isTemplateNameEmpty: true,
|
|
194
|
+
// Add errorMessage directly to the props
|
|
195
|
+
errorMessage: 'Please enter template name',
|
|
196
|
+
intl: {
|
|
197
|
+
formatMessage: () => 'Please enter template name',
|
|
198
|
+
},
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
const { container } = renderWithProviders(<EmailWrapperView {...props} />);
|
|
202
|
+
|
|
203
|
+
// Look for error feedback in the Ant Design form
|
|
204
|
+
const errorElement = container.querySelector('.ant-form-item-explain');
|
|
205
|
+
|
|
206
|
+
if (errorElement) {
|
|
207
|
+
expect(errorElement.textContent).toContain('Please enter template name');
|
|
208
|
+
} else {
|
|
209
|
+
// If the standard error element is not found, look for any element containing the error text
|
|
210
|
+
const allText = container.textContent;
|
|
211
|
+
expect(allText).toContain('Please enter template name');
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
});
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @jest-environment jsdom
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
// Tests for EmailWrapper container component
|
|
6
|
+
describe('EmailWrapper Container Tests', () => {
|
|
7
|
+
// This test verifies that the container passes props from the custom hook to the view component
|
|
8
|
+
it('should pass props from custom hook to view', () => {
|
|
9
|
+
// Mock hook results
|
|
10
|
+
const mockHookResults = {
|
|
11
|
+
templateName: 'Test Template',
|
|
12
|
+
isTemplateNameEmpty: false,
|
|
13
|
+
modeContent: { file: { name: 'test.zip' } },
|
|
14
|
+
modes: ['upload', 'editor'],
|
|
15
|
+
isShowEmailCreate: false,
|
|
16
|
+
emailProps: { location: { pathname: '/email/create' } },
|
|
17
|
+
cmsTemplatesProps: { cmsTemplates: [] },
|
|
18
|
+
uploadButtonLabel: 'Upload',
|
|
19
|
+
onTemplateNameChange: jest.fn(),
|
|
20
|
+
onChange: jest.fn(),
|
|
21
|
+
useFileUpload: jest.fn()
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
// Mock view component (captures props)
|
|
25
|
+
let capturedViewProps = null;
|
|
26
|
+
const mockView = jest.fn(props => {
|
|
27
|
+
capturedViewProps = props;
|
|
28
|
+
return null;
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
// Mock custom hook
|
|
32
|
+
const mockUseEmailWrapper = jest.fn(() => mockHookResults);
|
|
33
|
+
|
|
34
|
+
// Container props
|
|
35
|
+
const containerProps = {
|
|
36
|
+
isUploading: false,
|
|
37
|
+
emailCreateMode: 'editor',
|
|
38
|
+
step: 'modeSelection',
|
|
39
|
+
isFullMode: true,
|
|
40
|
+
EmailLayout: null
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
// Simulate the container component behavior
|
|
44
|
+
function simulateContainer() {
|
|
45
|
+
// Get state and handlers from hook
|
|
46
|
+
const hookResults = mockUseEmailWrapper(containerProps);
|
|
47
|
+
|
|
48
|
+
// Render view with combined props
|
|
49
|
+
return mockView({
|
|
50
|
+
...containerProps,
|
|
51
|
+
...hookResults
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Run the simulation
|
|
56
|
+
simulateContainer();
|
|
57
|
+
|
|
58
|
+
// Verify hook was called with correct props
|
|
59
|
+
expect(mockUseEmailWrapper).toHaveBeenCalledWith(containerProps);
|
|
60
|
+
|
|
61
|
+
// Verify view received combined props
|
|
62
|
+
expect(capturedViewProps).toEqual({
|
|
63
|
+
...containerProps,
|
|
64
|
+
...mockHookResults
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
// Verify specific props
|
|
68
|
+
expect(capturedViewProps.isUploading).toBe(containerProps.isUploading);
|
|
69
|
+
expect(capturedViewProps.emailCreateMode).toBe(containerProps.emailCreateMode);
|
|
70
|
+
expect(capturedViewProps.templateName).toBe(mockHookResults.templateName);
|
|
71
|
+
expect(capturedViewProps.modes).toBe(mockHookResults.modes);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
// This test verifies the pattern is maintainable for future changes
|
|
75
|
+
it('handles adding new hook properties', () => {
|
|
76
|
+
// Mock hook with additional property
|
|
77
|
+
const mockHookWithNewProp = jest.fn(() => ({
|
|
78
|
+
templateName: 'Test',
|
|
79
|
+
newFeature: 'This is a new feature' // New property
|
|
80
|
+
}));
|
|
81
|
+
|
|
82
|
+
// Mock view component (captures props)
|
|
83
|
+
let capturedViewProps = null;
|
|
84
|
+
const mockView = jest.fn(props => {
|
|
85
|
+
capturedViewProps = props;
|
|
86
|
+
return null;
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
// Simulate container with new hook property
|
|
90
|
+
function simulateContainer() {
|
|
91
|
+
const hookResults = mockHookWithNewProp({});
|
|
92
|
+
return mockView(hookResults);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Run the simulation
|
|
96
|
+
simulateContainer();
|
|
97
|
+
|
|
98
|
+
// Verify the new property is correctly passed to view
|
|
99
|
+
expect(capturedViewProps.newFeature).toBe('This is a new feature');
|
|
100
|
+
});
|
|
101
|
+
});
|