@capillarytech/creatives-library 8.0.298 → 8.0.299-alpha.4
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/package.json +1 -1
- package/services/api.js +17 -0
- package/services/tests/api.test.js +85 -0
- package/utils/commonUtils.js +10 -0
- package/utils/tests/commonUtil.test.js +169 -0
- package/v2Components/CommonTestAndPreview/AddTestCustomer.js +42 -0
- package/v2Components/CommonTestAndPreview/CustomerCreationModal.js +284 -0
- package/v2Components/CommonTestAndPreview/ExistingCustomerModal.js +72 -0
- package/v2Components/CommonTestAndPreview/SendTestMessage.js +78 -49
- package/v2Components/CommonTestAndPreview/_commonTestAndPreview.scss +200 -4
- package/v2Components/CommonTestAndPreview/actions.js +10 -0
- package/v2Components/CommonTestAndPreview/constants.js +18 -1
- package/v2Components/CommonTestAndPreview/index.js +274 -14
- package/v2Components/CommonTestAndPreview/messages.js +94 -0
- package/v2Components/CommonTestAndPreview/reducer.js +10 -0
- package/v2Components/CommonTestAndPreview/tests/AddTestCustomer.test.js +66 -0
- package/v2Components/CommonTestAndPreview/tests/CommonTestAndPreview.addTestCustomer.test.js +653 -0
- package/v2Components/CommonTestAndPreview/tests/CustomerCreationModal.test.js +316 -0
- package/v2Components/CommonTestAndPreview/tests/ExistingCustomerModal.test.js +114 -0
- package/v2Components/CommonTestAndPreview/tests/SendTestMessage.test.js +53 -0
- package/v2Components/CommonTestAndPreview/tests/constants.test.js +25 -2
- package/v2Components/CommonTestAndPreview/tests/index.test.js +7 -0
- package/v2Components/CommonTestAndPreview/tests/reducer.test.js +71 -0
- package/v2Components/CommonTestAndPreview/tests/selectors.test.js +17 -0
- package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +1588 -1336
- package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +369 -306
- package/v2Containers/TemplatesV2/TemplatesV2.style.js +9 -3
- package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +5794 -5080
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for CustomerCreationModal
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import React from 'react';
|
|
6
|
+
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
|
|
7
|
+
import { IntlProvider } from 'react-intl';
|
|
8
|
+
import PropTypes from 'prop-types';
|
|
9
|
+
import CustomerCreationModal from '../CustomerCreationModal';
|
|
10
|
+
import { CHANNELS, CUSTOMER_MODAL_NEW } from '../constants';
|
|
11
|
+
|
|
12
|
+
const mockGetMembersLookup = jest.fn();
|
|
13
|
+
jest.mock('../../../services/api', () => ({
|
|
14
|
+
getMembersLookup: (...args) => mockGetMembersLookup(...args),
|
|
15
|
+
}));
|
|
16
|
+
|
|
17
|
+
const mockMessages = {
|
|
18
|
+
'app.v2Components.TestAndPreviewSlidebox.customerCreationModalTitle': 'Add new test customer',
|
|
19
|
+
'app.v2Components.TestAndPreviewSlidebox.customerCreationModalDescription': 'This customer profile will be available for testing.',
|
|
20
|
+
'app.v2Components.TestAndPreviewSlidebox.customerName': 'Name',
|
|
21
|
+
'app.v2Components.TestAndPreviewSlidebox.customerNamePlaceholder': 'Enter the name',
|
|
22
|
+
'app.v2Components.TestAndPreviewSlidebox.customerEmailPlaceholder': 'Enter the Email',
|
|
23
|
+
'app.v2Components.TestAndPreviewSlidebox.customerMobilePlaceholder': 'Enter the Mobile Number',
|
|
24
|
+
'app.v2Components.TestAndPreviewSlidebox.saveButton': 'Save',
|
|
25
|
+
'app.v2Components.TestAndPreviewSlidebox.cancelButton': 'Cancel',
|
|
26
|
+
'app.v2Components.TestAndPreviewSlidebox.customerMobileNumber': 'Mobile Number',
|
|
27
|
+
'app.v2Components.TestAndPreviewSlidebox.emailAlreadyExists': 'This email already exists.',
|
|
28
|
+
'app.v2Components.TestAndPreviewSlidebox.mobileAlreadyExists': 'This mobile already exists.',
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const TestWrapper = ({ children }) => (
|
|
32
|
+
<IntlProvider locale="en" messages={mockMessages}>
|
|
33
|
+
{children}
|
|
34
|
+
</IntlProvider>
|
|
35
|
+
);
|
|
36
|
+
TestWrapper.propTypes = { children: PropTypes.node };
|
|
37
|
+
|
|
38
|
+
describe('CustomerCreationModal', () => {
|
|
39
|
+
const defaultProps = {
|
|
40
|
+
customerModal: [true, CUSTOMER_MODAL_NEW],
|
|
41
|
+
setCustomerModal: jest.fn(),
|
|
42
|
+
channel: CHANNELS.EMAIL,
|
|
43
|
+
customerData: { name: '', email: '', mobile: '' },
|
|
44
|
+
setCustomerData: jest.fn(),
|
|
45
|
+
onSave: jest.fn(),
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
beforeEach(() => {
|
|
49
|
+
jest.clearAllMocks();
|
|
50
|
+
mockGetMembersLookup.mockResolvedValue({ success: true, status: {}, response: { exists: false, customerDetails: [] } });
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('renders modal with title and form fields when visible', () => {
|
|
54
|
+
render(
|
|
55
|
+
<TestWrapper>
|
|
56
|
+
<CustomerCreationModal {...defaultProps} />
|
|
57
|
+
</TestWrapper>
|
|
58
|
+
);
|
|
59
|
+
expect(screen.getByText(/add new test customer/i)).toBeTruthy();
|
|
60
|
+
expect(screen.getByPlaceholderText(/enter the name/i)).toBeTruthy();
|
|
61
|
+
expect(screen.getByPlaceholderText(/enter the email/i)).toBeTruthy();
|
|
62
|
+
expect(screen.getByPlaceholderText(/enter the mobile number/i)).toBeTruthy();
|
|
63
|
+
expect(screen.getByRole('button', { name: /save/i })).toBeTruthy();
|
|
64
|
+
expect(screen.getByRole('button', { name: /cancel/i })).toBeTruthy();
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('calls setCustomerModal when Cancel is clicked', () => {
|
|
68
|
+
render(
|
|
69
|
+
<TestWrapper>
|
|
70
|
+
<CustomerCreationModal {...defaultProps} />
|
|
71
|
+
</TestWrapper>
|
|
72
|
+
);
|
|
73
|
+
fireEvent.click(screen.getByRole('button', { name: /cancel/i }));
|
|
74
|
+
expect(defaultProps.setCustomerModal).toHaveBeenCalledWith([false, '']);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('calls setCustomerData when name input changes', () => {
|
|
78
|
+
render(
|
|
79
|
+
<TestWrapper>
|
|
80
|
+
<CustomerCreationModal {...defaultProps} />
|
|
81
|
+
</TestWrapper>
|
|
82
|
+
);
|
|
83
|
+
fireEvent.change(screen.getByPlaceholderText(/enter the name/i), { target: { value: 'John' } });
|
|
84
|
+
expect(defaultProps.setCustomerData).toHaveBeenCalledWith(expect.objectContaining({ name: 'John' }));
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('calls onSave when Save is clicked with no validation errors', async () => {
|
|
88
|
+
render(
|
|
89
|
+
<TestWrapper>
|
|
90
|
+
<CustomerCreationModal {...defaultProps} customerData={{ name: '', email: 'a@b.co', mobile: '' }} />
|
|
91
|
+
</TestWrapper>
|
|
92
|
+
);
|
|
93
|
+
const saveBtn = screen.getByRole('button', { name: /save/i });
|
|
94
|
+
fireEvent.click(saveBtn);
|
|
95
|
+
await waitFor(() => expect(defaultProps.onSave).toHaveBeenCalled());
|
|
96
|
+
expect(defaultProps.onSave).toHaveBeenCalledWith(expect.any(Object), expect.any(Function));
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('formats mobile input with digits only when channel is SMS', () => {
|
|
100
|
+
const setCustomerData = jest.fn();
|
|
101
|
+
render(
|
|
102
|
+
<TestWrapper>
|
|
103
|
+
<CustomerCreationModal
|
|
104
|
+
{...defaultProps}
|
|
105
|
+
channel={CHANNELS.SMS}
|
|
106
|
+
setCustomerData={setCustomerData}
|
|
107
|
+
customerData={{ name: '', email: '', mobile: '' }}
|
|
108
|
+
/>
|
|
109
|
+
</TestWrapper>
|
|
110
|
+
);
|
|
111
|
+
const mobileInput = screen.getByPlaceholderText(/enter the mobile number/i);
|
|
112
|
+
fireEvent.change(mobileInput, { target: { value: '91 234 567 890' } });
|
|
113
|
+
expect(setCustomerData).toHaveBeenCalledWith(expect.objectContaining({ mobile: '91234567890' }));
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it('shows validation error for invalid email after debounce', async () => {
|
|
117
|
+
jest.useFakeTimers();
|
|
118
|
+
render(
|
|
119
|
+
<TestWrapper>
|
|
120
|
+
<CustomerCreationModal {...defaultProps} customerData={{ name: '', email: '', mobile: '' }} />
|
|
121
|
+
</TestWrapper>
|
|
122
|
+
);
|
|
123
|
+
const emailInput = screen.getByPlaceholderText(/enter the email/i);
|
|
124
|
+
fireEvent.change(emailInput, { target: { value: 'invalid' } });
|
|
125
|
+
jest.advanceTimersByTime(600);
|
|
126
|
+
await waitFor(() => {
|
|
127
|
+
expect(screen.queryByText(/please enter a valid email address/i)).toBeTruthy();
|
|
128
|
+
});
|
|
129
|
+
jest.useRealTimers();
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it('shows emailAlreadyExists when valid email exists in system', async () => {
|
|
133
|
+
jest.useFakeTimers();
|
|
134
|
+
mockGetMembersLookup.mockResolvedValue({ success: true, status: {}, response: { exists: true, customerDetails: [] } });
|
|
135
|
+
render(
|
|
136
|
+
<TestWrapper>
|
|
137
|
+
<CustomerCreationModal {...defaultProps} customerData={{ name: '', email: '', mobile: '' }} />
|
|
138
|
+
</TestWrapper>
|
|
139
|
+
);
|
|
140
|
+
const emailInput = screen.getByPlaceholderText(/enter the email/i);
|
|
141
|
+
fireEvent.change(emailInput, { target: { value: 'user@example.com' } });
|
|
142
|
+
jest.advanceTimersByTime(600);
|
|
143
|
+
await waitFor(() => {
|
|
144
|
+
expect(mockGetMembersLookup).toHaveBeenCalledWith('email', 'user@example.com');
|
|
145
|
+
});
|
|
146
|
+
await waitFor(() => {
|
|
147
|
+
expect(screen.queryByText(/this email already exists/i)).toBeTruthy();
|
|
148
|
+
}, { timeout: 1000 });
|
|
149
|
+
jest.useRealTimers();
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
it('clears email error when email is cleared', async () => {
|
|
153
|
+
jest.useFakeTimers();
|
|
154
|
+
render(
|
|
155
|
+
<TestWrapper>
|
|
156
|
+
<CustomerCreationModal {...defaultProps} customerData={{ name: '', email: 'bad', mobile: '' }} />
|
|
157
|
+
</TestWrapper>
|
|
158
|
+
);
|
|
159
|
+
const emailInput = screen.getByPlaceholderText(/enter the email/i);
|
|
160
|
+
fireEvent.change(emailInput, { target: { value: '' } });
|
|
161
|
+
jest.advanceTimersByTime(600);
|
|
162
|
+
await waitFor(() => {
|
|
163
|
+
expect(screen.queryByText(/please enter a valid email address/i)).toBeFalsy();
|
|
164
|
+
});
|
|
165
|
+
jest.useRealTimers();
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it('performLookup catch returns success false and does not set exists error', async () => {
|
|
169
|
+
jest.useFakeTimers();
|
|
170
|
+
const consoleSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
|
171
|
+
mockGetMembersLookup.mockRejectedValue(new Error('Network error'));
|
|
172
|
+
render(
|
|
173
|
+
<TestWrapper>
|
|
174
|
+
<CustomerCreationModal {...defaultProps} customerData={{ name: '', email: '', mobile: '' }} />
|
|
175
|
+
</TestWrapper>
|
|
176
|
+
);
|
|
177
|
+
const emailInput = screen.getByPlaceholderText(/enter the email/i);
|
|
178
|
+
fireEvent.change(emailInput, { target: { value: 'user@example.com' } });
|
|
179
|
+
jest.advanceTimersByTime(600);
|
|
180
|
+
await waitFor(() => expect(mockGetMembersLookup).toHaveBeenCalled());
|
|
181
|
+
await waitFor(() => {
|
|
182
|
+
expect(screen.queryByText(/this email already exists/i)).toBeFalsy();
|
|
183
|
+
});
|
|
184
|
+
consoleSpy.mockRestore();
|
|
185
|
+
jest.useRealTimers();
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
it('shows validation error for invalid mobile when channel is SMS', async () => {
|
|
189
|
+
jest.useFakeTimers();
|
|
190
|
+
render(
|
|
191
|
+
<TestWrapper>
|
|
192
|
+
<CustomerCreationModal
|
|
193
|
+
{...defaultProps}
|
|
194
|
+
channel={CHANNELS.SMS}
|
|
195
|
+
customerData={{ name: '', email: '', mobile: '' }}
|
|
196
|
+
/>
|
|
197
|
+
</TestWrapper>
|
|
198
|
+
);
|
|
199
|
+
const mobileInput = screen.getByPlaceholderText(/enter the mobile number/i);
|
|
200
|
+
fireEvent.change(mobileInput, { target: { value: '12' } });
|
|
201
|
+
jest.advanceTimersByTime(600);
|
|
202
|
+
await waitFor(() => {
|
|
203
|
+
expect(screen.queryByText(/please enter a valid mobile number/i)).toBeTruthy();
|
|
204
|
+
}, { timeout: 500 });
|
|
205
|
+
jest.useRealTimers();
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
it('Save button is disabled when validation errors present', async () => {
|
|
209
|
+
jest.useFakeTimers();
|
|
210
|
+
render(
|
|
211
|
+
<TestWrapper>
|
|
212
|
+
<CustomerCreationModal {...defaultProps} customerData={{ name: '', email: 'invalid', mobile: '' }} />
|
|
213
|
+
</TestWrapper>
|
|
214
|
+
);
|
|
215
|
+
const emailInput = screen.getByPlaceholderText(/enter the email/i);
|
|
216
|
+
fireEvent.change(emailInput, { target: { value: 'bad' } });
|
|
217
|
+
jest.advanceTimersByTime(600);
|
|
218
|
+
await waitFor(() => {
|
|
219
|
+
const saveBtn = screen.getByRole('button', { name: /save/i });
|
|
220
|
+
expect(saveBtn.disabled).toBe(true);
|
|
221
|
+
});
|
|
222
|
+
jest.useRealTimers();
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
it('clears email error when valid email does not exist in system', async () => {
|
|
226
|
+
jest.useFakeTimers();
|
|
227
|
+
mockGetMembersLookup.mockResolvedValue({ success: true, status: {}, response: { exists: false, customerDetails: [] } });
|
|
228
|
+
render(
|
|
229
|
+
<TestWrapper>
|
|
230
|
+
<CustomerCreationModal {...defaultProps} customerData={{ name: '', email: '', mobile: '' }} />
|
|
231
|
+
</TestWrapper>
|
|
232
|
+
);
|
|
233
|
+
const emailInput = screen.getByPlaceholderText(/enter the email/i);
|
|
234
|
+
fireEvent.change(emailInput, { target: { value: 'newuser@example.com' } });
|
|
235
|
+
jest.advanceTimersByTime(600);
|
|
236
|
+
await waitFor(() => expect(mockGetMembersLookup).toHaveBeenCalledWith('email', 'newuser@example.com'));
|
|
237
|
+
await waitFor(() => {
|
|
238
|
+
expect(screen.queryByText(/this email already exists/i)).toBeFalsy();
|
|
239
|
+
}, { timeout: 500 });
|
|
240
|
+
jest.useRealTimers();
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
it('shows mobileAlreadyExists when valid mobile exists in system', async () => {
|
|
244
|
+
jest.useFakeTimers();
|
|
245
|
+
mockGetMembersLookup.mockResolvedValue({ success: true, status: {}, response: { exists: true, customerDetails: [] } });
|
|
246
|
+
render(
|
|
247
|
+
<TestWrapper>
|
|
248
|
+
<CustomerCreationModal
|
|
249
|
+
{...defaultProps}
|
|
250
|
+
channel={CHANNELS.SMS}
|
|
251
|
+
customerData={{ name: '', email: '', mobile: '' }}
|
|
252
|
+
/>
|
|
253
|
+
</TestWrapper>
|
|
254
|
+
);
|
|
255
|
+
const mobileInput = screen.getByPlaceholderText(/enter the mobile number/i);
|
|
256
|
+
fireEvent.change(mobileInput, { target: { value: '9123456789' } });
|
|
257
|
+
jest.advanceTimersByTime(600);
|
|
258
|
+
await waitFor(() => expect(mockGetMembersLookup).toHaveBeenCalledWith('mobile', '9123456789'));
|
|
259
|
+
await waitFor(() => {
|
|
260
|
+
expect(screen.queryByText(/this mobile already exists/i)).toBeTruthy();
|
|
261
|
+
}, { timeout: 1000 });
|
|
262
|
+
jest.useRealTimers();
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
it('clears previous email validation timeout when email changes again before debounce', () => {
|
|
266
|
+
jest.useFakeTimers();
|
|
267
|
+
mockGetMembersLookup.mockResolvedValue({ success: true, status: {}, response: { exists: false, customerDetails: [] } });
|
|
268
|
+
render(
|
|
269
|
+
<TestWrapper>
|
|
270
|
+
<CustomerCreationModal {...defaultProps} customerData={{ name: '', email: '', mobile: '' }} />
|
|
271
|
+
</TestWrapper>
|
|
272
|
+
);
|
|
273
|
+
const emailInput = screen.getByPlaceholderText(/enter the email/i);
|
|
274
|
+
fireEvent.change(emailInput, { target: { value: 'first@example.com' } });
|
|
275
|
+
jest.advanceTimersByTime(200);
|
|
276
|
+
fireEvent.change(emailInput, { target: { value: 'second@example.com' } });
|
|
277
|
+
jest.advanceTimersByTime(600);
|
|
278
|
+
expect(mockGetMembersLookup).toHaveBeenCalledWith('email', 'second@example.com');
|
|
279
|
+
jest.useRealTimers();
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
it('clears previous mobile validation timeout when mobile changes again before debounce', () => {
|
|
283
|
+
jest.useFakeTimers();
|
|
284
|
+
mockGetMembersLookup.mockResolvedValue({ success: true, status: {}, response: { exists: false, customerDetails: [] } });
|
|
285
|
+
render(
|
|
286
|
+
<TestWrapper>
|
|
287
|
+
<CustomerCreationModal
|
|
288
|
+
{...defaultProps}
|
|
289
|
+
channel={CHANNELS.SMS}
|
|
290
|
+
customerData={{ name: '', email: '', mobile: '' }}
|
|
291
|
+
/>
|
|
292
|
+
</TestWrapper>
|
|
293
|
+
);
|
|
294
|
+
const mobileInput = screen.getByPlaceholderText(/enter the mobile number/i);
|
|
295
|
+
fireEvent.change(mobileInput, { target: { value: '9111111111' } });
|
|
296
|
+
jest.advanceTimersByTime(200);
|
|
297
|
+
fireEvent.change(mobileInput, { target: { value: '9222222222' } });
|
|
298
|
+
jest.advanceTimersByTime(600);
|
|
299
|
+
expect(mockGetMembersLookup).toHaveBeenCalledWith('mobile', '9222222222');
|
|
300
|
+
jest.useRealTimers();
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
it('cleans up timeouts on unmount', () => {
|
|
304
|
+
jest.useFakeTimers();
|
|
305
|
+
const { unmount } = render(
|
|
306
|
+
<TestWrapper>
|
|
307
|
+
<CustomerCreationModal {...defaultProps} customerData={{ name: '', email: '', mobile: '' }} />
|
|
308
|
+
</TestWrapper>
|
|
309
|
+
);
|
|
310
|
+
const emailInput = screen.getByPlaceholderText(/enter the email/i);
|
|
311
|
+
fireEvent.change(emailInput, { target: { value: 'unmount@example.com' } });
|
|
312
|
+
unmount();
|
|
313
|
+
jest.advanceTimersByTime(1000);
|
|
314
|
+
jest.useRealTimers();
|
|
315
|
+
});
|
|
316
|
+
});
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for ExistingCustomerModal
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import React from 'react';
|
|
6
|
+
import { render, screen, fireEvent } from '@testing-library/react';
|
|
7
|
+
import { IntlProvider } from 'react-intl';
|
|
8
|
+
import PropTypes from 'prop-types';
|
|
9
|
+
import ExistingCustomerModal from '../ExistingCustomerModal';
|
|
10
|
+
import { CHANNELS, CUSTOMER_MODAL_EXISTING } from '../constants';
|
|
11
|
+
|
|
12
|
+
const mockMessages = {
|
|
13
|
+
'app.v2Components.TestAndPreviewSlidebox.customerCreationModalTitle': 'Add new test customer',
|
|
14
|
+
'app.v2Components.TestAndPreviewSlidebox.existingCustomerModalDescription': 'This user profile already exists in the system.',
|
|
15
|
+
'app.v2Components.TestAndPreviewSlidebox.saveButton': 'Save',
|
|
16
|
+
'app.v2Components.TestAndPreviewSlidebox.cancelButton': 'Cancel',
|
|
17
|
+
'app.v2Components.TestAndPreviewSlidebox.customerEmail': 'Email',
|
|
18
|
+
'app.v2Components.TestAndPreviewSlidebox.customerMobileNumber': 'Mobile Number',
|
|
19
|
+
'app.v2Components.TestAndPreviewSlidebox.customerID': 'Customer ID',
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const TestWrapper = ({ children }) => (
|
|
23
|
+
<IntlProvider locale="en" messages={mockMessages}>
|
|
24
|
+
{children}
|
|
25
|
+
</IntlProvider>
|
|
26
|
+
);
|
|
27
|
+
TestWrapper.propTypes = { children: PropTypes.node };
|
|
28
|
+
|
|
29
|
+
describe('ExistingCustomerModal', () => {
|
|
30
|
+
const defaultProps = {
|
|
31
|
+
customerModal: [true, CUSTOMER_MODAL_EXISTING],
|
|
32
|
+
setCustomerModal: jest.fn(),
|
|
33
|
+
customerData: { name: 'John Doe', email: 'john@example.com', mobile: '', customerId: 'cust-123' },
|
|
34
|
+
channel: CHANNELS.EMAIL,
|
|
35
|
+
onSave: jest.fn(),
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
beforeEach(() => {
|
|
39
|
+
jest.clearAllMocks();
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('renders modal with customer details when visible', () => {
|
|
43
|
+
render(
|
|
44
|
+
<TestWrapper>
|
|
45
|
+
<ExistingCustomerModal {...defaultProps} />
|
|
46
|
+
</TestWrapper>
|
|
47
|
+
);
|
|
48
|
+
expect(screen.getByText(/add new test customer/i)).toBeTruthy();
|
|
49
|
+
expect(screen.getByText(/this user profile already exists/i)).toBeTruthy();
|
|
50
|
+
expect(screen.getByText('John Doe')).toBeTruthy();
|
|
51
|
+
expect(screen.getByText(/john@example.com/)).toBeTruthy();
|
|
52
|
+
expect(screen.getByText(/cust-123/)).toBeTruthy();
|
|
53
|
+
expect(screen.getByRole('button', { name: /save/i })).toBeTruthy();
|
|
54
|
+
expect(screen.getByRole('button', { name: /cancel/i })).toBeTruthy();
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('shows mobile when channel is SMS', () => {
|
|
58
|
+
render(
|
|
59
|
+
<TestWrapper>
|
|
60
|
+
<ExistingCustomerModal
|
|
61
|
+
{...defaultProps}
|
|
62
|
+
channel={CHANNELS.SMS}
|
|
63
|
+
customerData={{ name: 'Jane', email: '', mobile: '9123456789', customerId: 'cust-456' }}
|
|
64
|
+
/>
|
|
65
|
+
</TestWrapper>
|
|
66
|
+
);
|
|
67
|
+
expect(screen.getByText(/9123456789/)).toBeTruthy();
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('calls setCustomerModal when Cancel is clicked', () => {
|
|
71
|
+
render(
|
|
72
|
+
<TestWrapper>
|
|
73
|
+
<ExistingCustomerModal {...defaultProps} />
|
|
74
|
+
</TestWrapper>
|
|
75
|
+
);
|
|
76
|
+
fireEvent.click(screen.getByRole('button', { name: /cancel/i }));
|
|
77
|
+
expect(defaultProps.setCustomerModal).toHaveBeenCalledWith([false, '']);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('calls onSave when Save is clicked', () => {
|
|
81
|
+
render(
|
|
82
|
+
<TestWrapper>
|
|
83
|
+
<ExistingCustomerModal {...defaultProps} />
|
|
84
|
+
</TestWrapper>
|
|
85
|
+
);
|
|
86
|
+
fireEvent.click(screen.getByRole('button', { name: /save/i }));
|
|
87
|
+
expect(defaultProps.onSave).toHaveBeenCalled();
|
|
88
|
+
expect(defaultProps.onSave.mock.calls[0][0]).toEqual({});
|
|
89
|
+
expect(typeof defaultProps.onSave.mock.calls[0][1]).toBe('function');
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it('shows dash for missing name', () => {
|
|
93
|
+
render(
|
|
94
|
+
<TestWrapper>
|
|
95
|
+
<ExistingCustomerModal {...defaultProps} customerData={{ name: '', email: 'a@b.co', mobile: '', customerId: 'c1' }} />
|
|
96
|
+
</TestWrapper>
|
|
97
|
+
);
|
|
98
|
+
expect(screen.getByText('-')).toBeTruthy();
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it('does not show email row when channel is EMAIL but customerData.email is empty', () => {
|
|
102
|
+
render(
|
|
103
|
+
<TestWrapper>
|
|
104
|
+
<ExistingCustomerModal
|
|
105
|
+
{...defaultProps}
|
|
106
|
+
customerData={{ name: 'No Email', email: '', mobile: '', customerId: 'c2' }}
|
|
107
|
+
/>
|
|
108
|
+
</TestWrapper>
|
|
109
|
+
);
|
|
110
|
+
expect(screen.getByText('No Email')).toBeTruthy();
|
|
111
|
+
expect(screen.getByText('Customer ID')).toBeTruthy();
|
|
112
|
+
expect(screen.getByText('c2')).toBeTruthy();
|
|
113
|
+
});
|
|
114
|
+
});
|
|
@@ -135,6 +135,10 @@ describe('SendTestMessage', () => {
|
|
|
135
135
|
isLoadingSenderDetails: false,
|
|
136
136
|
smsTraiDltEnabled: false,
|
|
137
137
|
registeredSenderIds: [],
|
|
138
|
+
channel: 'EMAIL',
|
|
139
|
+
renderAddTestCustomerButton: jest.fn(() => null),
|
|
140
|
+
searchValue: '',
|
|
141
|
+
setSearchValue: jest.fn(),
|
|
138
142
|
};
|
|
139
143
|
|
|
140
144
|
beforeEach(() => {
|
|
@@ -633,4 +637,53 @@ describe('SendTestMessage', () => {
|
|
|
633
637
|
consoleSpy.mockRestore();
|
|
634
638
|
});
|
|
635
639
|
});
|
|
640
|
+
|
|
641
|
+
describe('notFoundContent (Add as test customer)', () => {
|
|
642
|
+
it('should call renderAddTestCustomerButton and use result as notFoundContent', () => {
|
|
643
|
+
const renderAddTestCustomerButton = jest.fn(() => (
|
|
644
|
+
<button type="button" data-testid="add-test-customer-btn">
|
|
645
|
+
Add as test customer
|
|
646
|
+
</button>
|
|
647
|
+
));
|
|
648
|
+
const props = {
|
|
649
|
+
...defaultProps,
|
|
650
|
+
renderAddTestCustomerButton,
|
|
651
|
+
};
|
|
652
|
+
render(
|
|
653
|
+
<TestWrapper>
|
|
654
|
+
<SendTestMessage {...props} />
|
|
655
|
+
</TestWrapper>
|
|
656
|
+
);
|
|
657
|
+
expect(renderAddTestCustomerButton).toHaveBeenCalled();
|
|
658
|
+
});
|
|
659
|
+
|
|
660
|
+
it('should render notFoundContent as a clickable button when provided', () => {
|
|
661
|
+
const onAddClick = jest.fn();
|
|
662
|
+
const renderAddTestCustomerButton = jest.fn(() => (
|
|
663
|
+
<button type="button" data-testid="add-test-customer-btn" onClick={onAddClick}>
|
|
664
|
+
Add as test customer
|
|
665
|
+
</button>
|
|
666
|
+
));
|
|
667
|
+
const props = {
|
|
668
|
+
...defaultProps,
|
|
669
|
+
renderAddTestCustomerButton,
|
|
670
|
+
};
|
|
671
|
+
const { container } = render(
|
|
672
|
+
<TestWrapper>
|
|
673
|
+
<SendTestMessage {...props} />
|
|
674
|
+
</TestWrapper>
|
|
675
|
+
);
|
|
676
|
+
// notFoundContent is passed to TreeSelect and shown when dropdown is open with no match.
|
|
677
|
+
// We assert the render function returns a button that can be clicked.
|
|
678
|
+
const notFoundContent = renderAddTestCustomerButton.mock.results[0]?.value;
|
|
679
|
+
expect(notFoundContent).toBeTruthy();
|
|
680
|
+
expect(notFoundContent.props['data-testid']).toBe('add-test-customer-btn');
|
|
681
|
+
expect(notFoundContent.type).toBe('button');
|
|
682
|
+
// Simulate click on the returned element's onClick
|
|
683
|
+
if (notFoundContent.props.onClick) {
|
|
684
|
+
notFoundContent.props.onClick();
|
|
685
|
+
expect(onAddClick).toHaveBeenCalled();
|
|
686
|
+
}
|
|
687
|
+
});
|
|
688
|
+
});
|
|
636
689
|
});
|
|
@@ -54,8 +54,13 @@ import {
|
|
|
54
54
|
// API Channel Constants
|
|
55
55
|
API_CHANNEL_PUSH,
|
|
56
56
|
// Identifier Type Constants
|
|
57
|
-
IDENTIFIER_TYPE_MOBILE,
|
|
58
57
|
IDENTIFIER_TYPE_EMAIL,
|
|
58
|
+
IDENTIFIER_TYPE_MOBILE,
|
|
59
|
+
IDENTIFIER_TYPE_PHONE,
|
|
60
|
+
INPUT_HAS_ERROR_CLASS,
|
|
61
|
+
// Validation Regex
|
|
62
|
+
EMAIL_REGEX,
|
|
63
|
+
PHONE_REGEX,
|
|
59
64
|
// Channel Name Constants
|
|
60
65
|
CHANNEL_NAME_INAPP,
|
|
61
66
|
// Channel Mapping Constants
|
|
@@ -208,8 +213,26 @@ describe('CommonTestAndPreview Constants', () => {
|
|
|
208
213
|
|
|
209
214
|
describe('Identifier Type Constants', () => {
|
|
210
215
|
it('should export identifier type constants', () => {
|
|
211
|
-
expect(IDENTIFIER_TYPE_MOBILE).toBe('mobile');
|
|
212
216
|
expect(IDENTIFIER_TYPE_EMAIL).toBe('email');
|
|
217
|
+
expect(IDENTIFIER_TYPE_MOBILE).toBe('mobile');
|
|
218
|
+
expect(IDENTIFIER_TYPE_PHONE).toBe('phone');
|
|
219
|
+
});
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
describe('Validation Regex', () => {
|
|
223
|
+
it('should export EMAIL_REGEX that validates email format', () => {
|
|
224
|
+
expect(EMAIL_REGEX.test('user@example.com')).toBe(true);
|
|
225
|
+
expect(EMAIL_REGEX.test('invalid')).toBe(false);
|
|
226
|
+
});
|
|
227
|
+
it('should export PHONE_REGEX that validates phone format', () => {
|
|
228
|
+
expect(PHONE_REGEX.test('9123456789')).toBe(true);
|
|
229
|
+
expect(PHONE_REGEX.test('123')).toBe(false);
|
|
230
|
+
});
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
describe('Input has error class', () => {
|
|
234
|
+
it('should export INPUT_HAS_ERROR_CLASS', () => {
|
|
235
|
+
expect(INPUT_HAS_ERROR_CLASS).toBe(' has-error');
|
|
213
236
|
});
|
|
214
237
|
});
|
|
215
238
|
|
|
@@ -81,6 +81,12 @@ jest.mock('../../../utils/cdnTransformation', () => ({
|
|
|
81
81
|
getCdnUrl: jest.fn(({ url }) => `cdn_${url}`),
|
|
82
82
|
}));
|
|
83
83
|
|
|
84
|
+
// Mock services/api for add test customer flow
|
|
85
|
+
jest.mock('../../../services/api', () => ({
|
|
86
|
+
getMembersLookup: jest.fn(),
|
|
87
|
+
createTestCustomer: jest.fn(),
|
|
88
|
+
}));
|
|
89
|
+
|
|
84
90
|
// Mock messages - using actual message IDs from messages.js
|
|
85
91
|
const mockMessages = {
|
|
86
92
|
'app.v2Components.TestAndPreviewSlidebox.testAndPreviewHeader': { defaultMessage: 'Preview and Test' },
|
|
@@ -126,6 +132,7 @@ describe('CommonTestAndPreview', () => {
|
|
|
126
132
|
clearPreviewErrors: jest.fn(),
|
|
127
133
|
getSenderDetailsRequested: jest.fn(),
|
|
128
134
|
getWeCrmAccountsRequested: jest.fn(),
|
|
135
|
+
addTestCustomer: jest.fn(),
|
|
129
136
|
};
|
|
130
137
|
|
|
131
138
|
const defaultProps = {
|
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
GET_TEST_CUSTOMERS_REQUESTED,
|
|
25
25
|
GET_TEST_CUSTOMERS_SUCCESS,
|
|
26
26
|
GET_TEST_CUSTOMERS_FAILURE,
|
|
27
|
+
ADD_TEST_CUSTOMER,
|
|
27
28
|
GET_TEST_GROUPS_REQUESTED,
|
|
28
29
|
GET_TEST_GROUPS_SUCCESS,
|
|
29
30
|
GET_TEST_GROUPS_FAILURE,
|
|
@@ -611,6 +612,76 @@ describe('previewAndTestReducer', () => {
|
|
|
611
612
|
});
|
|
612
613
|
});
|
|
613
614
|
|
|
615
|
+
describe('ADD_TEST_CUSTOMER', () => {
|
|
616
|
+
it('should add new customer to testCustomers list', () => {
|
|
617
|
+
const customer = {
|
|
618
|
+
userId: 'cust-1',
|
|
619
|
+
customerId: 'cust-1',
|
|
620
|
+
name: 'John',
|
|
621
|
+
email: 'john@example.com',
|
|
622
|
+
mobile: '',
|
|
623
|
+
};
|
|
624
|
+
const action = {
|
|
625
|
+
type: ADD_TEST_CUSTOMER,
|
|
626
|
+
payload: { customer },
|
|
627
|
+
};
|
|
628
|
+
const result = previewAndTestReducer(initialState, action);
|
|
629
|
+
|
|
630
|
+
const list = result.get('testCustomers');
|
|
631
|
+
expect(Array.isArray(list) ? list.length : list.size).toBe(1);
|
|
632
|
+
const first = Array.isArray(list) ? list[0] : list.get(0);
|
|
633
|
+
const id = first.userId != null ? first.userId : first.get('userId');
|
|
634
|
+
const name = first.name != null ? first.name : first.get('name');
|
|
635
|
+
expect(id).toBe('cust-1');
|
|
636
|
+
expect(name).toBe('John');
|
|
637
|
+
});
|
|
638
|
+
|
|
639
|
+
it('should not add duplicate customer when userId already in list', () => {
|
|
640
|
+
const existing = fromJS([
|
|
641
|
+
{ userId: 'cust-1', customerId: 'cust-1', name: 'John', email: 'john@example.com', mobile: '' },
|
|
642
|
+
]);
|
|
643
|
+
const stateWithCustomer = initialState.set('testCustomers', existing);
|
|
644
|
+
const customer = {
|
|
645
|
+
userId: 'cust-1',
|
|
646
|
+
customerId: 'cust-1',
|
|
647
|
+
name: 'John Updated',
|
|
648
|
+
email: 'john@example.com',
|
|
649
|
+
mobile: '',
|
|
650
|
+
};
|
|
651
|
+
const action = {
|
|
652
|
+
type: ADD_TEST_CUSTOMER,
|
|
653
|
+
payload: { customer },
|
|
654
|
+
};
|
|
655
|
+
const result = previewAndTestReducer(stateWithCustomer, action);
|
|
656
|
+
|
|
657
|
+
const list = result.get('testCustomers');
|
|
658
|
+
expect(Array.isArray(list) ? list.length : list.size).toBe(1);
|
|
659
|
+
const first = Array.isArray(list) ? list[0] : list.get(0);
|
|
660
|
+
const name = first.name != null ? first.name : first.get('name');
|
|
661
|
+
expect(name).toBe('John');
|
|
662
|
+
});
|
|
663
|
+
|
|
664
|
+
it('should use customerId when userId is missing', () => {
|
|
665
|
+
const customer = {
|
|
666
|
+
customerId: 'cust-2',
|
|
667
|
+
name: 'Jane',
|
|
668
|
+
email: 'jane@example.com',
|
|
669
|
+
mobile: '',
|
|
670
|
+
};
|
|
671
|
+
const action = {
|
|
672
|
+
type: ADD_TEST_CUSTOMER,
|
|
673
|
+
payload: { customer },
|
|
674
|
+
};
|
|
675
|
+
const result = previewAndTestReducer(initialState, action);
|
|
676
|
+
|
|
677
|
+
const list = result.get('testCustomers');
|
|
678
|
+
expect(Array.isArray(list) ? list.length : list.size).toBe(1);
|
|
679
|
+
const first = Array.isArray(list) ? list[0] : list.get(0);
|
|
680
|
+
const id = first.customerId != null ? first.customerId : first.get('customerId');
|
|
681
|
+
expect(id).toBe('cust-2');
|
|
682
|
+
});
|
|
683
|
+
});
|
|
684
|
+
|
|
614
685
|
describe('GET_TEST_GROUPS_REQUESTED', () => {
|
|
615
686
|
it('should set fetching flag and clear error', () => {
|
|
616
687
|
const action = { type: GET_TEST_GROUPS_REQUESTED };
|
|
@@ -431,6 +431,23 @@ describe('CommonTestAndPreview Selectors', () => {
|
|
|
431
431
|
// Should handle null gracefully - returns null when prefilledValues is null
|
|
432
432
|
expect(result).toBeNull();
|
|
433
433
|
});
|
|
434
|
+
|
|
435
|
+
it('should return undefined when commonTestAndPreview substate is missing', () => {
|
|
436
|
+
const selector = makeSelectPrefilledValues();
|
|
437
|
+
const result = selector(fromJS({}));
|
|
438
|
+
|
|
439
|
+
expect(result).toBeUndefined();
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
it('should return null when prefilledValues is undefined', () => {
|
|
443
|
+
const stateWithUndefined = fromJS({
|
|
444
|
+
commonTestAndPreview: {},
|
|
445
|
+
});
|
|
446
|
+
const selector = makeSelectPrefilledValues();
|
|
447
|
+
const result = selector(stateWithUndefined);
|
|
448
|
+
|
|
449
|
+
expect(result).toBeNull();
|
|
450
|
+
});
|
|
434
451
|
});
|
|
435
452
|
|
|
436
453
|
describe('makeSelectTestMessageResponse', () => {
|