@capillarytech/creatives-library 8.0.309 → 8.0.310
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/constants/unified.js +1 -5
- package/initialState.js +2 -0
- package/package.json +1 -1
- package/services/api.js +0 -17
- package/services/tests/api.test.js +0 -85
- package/utils/common.js +8 -5
- package/utils/commonUtils.js +93 -46
- package/utils/tagValidations.js +223 -83
- package/utils/tests/commonUtil.test.js +124 -316
- package/utils/tests/tagValidations.test.js +358 -441
- package/v2Components/CommonTestAndPreview/SendTestMessage.js +49 -78
- package/v2Components/CommonTestAndPreview/_commonTestAndPreview.scss +34 -134
- package/v2Components/CommonTestAndPreview/actions.js +0 -10
- package/v2Components/CommonTestAndPreview/constants.js +1 -15
- package/v2Components/CommonTestAndPreview/index.js +19 -80
- package/v2Components/CommonTestAndPreview/messages.js +0 -94
- package/v2Components/CommonTestAndPreview/reducer.js +0 -10
- package/v2Components/CommonTestAndPreview/tests/SendTestMessage.test.js +0 -53
- package/v2Components/CommonTestAndPreview/tests/constants.test.js +1 -31
- package/v2Components/CommonTestAndPreview/tests/index.test.js +0 -36
- package/v2Components/CommonTestAndPreview/tests/reducer.test.js +0 -71
- package/v2Components/CommonTestAndPreview/tests/sagas.test.js +0 -377
- package/v2Components/CommonTestAndPreview/tests/selectors.test.js +0 -17
- package/v2Components/ErrorInfoNote/index.js +5 -2
- package/v2Components/FormBuilder/index.js +203 -137
- package/v2Components/FormBuilder/messages.js +8 -0
- package/v2Components/HtmlEditor/HTMLEditor.js +5 -0
- package/v2Components/HtmlEditor/__tests__/HTMLEditor.apiErrors.test.js +1 -0
- package/v2Components/HtmlEditor/__tests__/HTMLEditor.test.js +15 -0
- package/v2Components/HtmlEditor/components/CodeEditorPane/index.js +2 -1
- package/v2Containers/Cap/mockData.js +14 -0
- package/v2Containers/Cap/reducer.js +55 -3
- package/v2Containers/Cap/tests/reducer.test.js +102 -0
- package/v2Containers/CreativesContainer/SlideBoxContent.js +1 -5
- package/v2Containers/CreativesContainer/SlideBoxFooter.js +5 -13
- package/v2Containers/CreativesContainer/constants.js +0 -6
- package/v2Containers/CreativesContainer/index.js +7 -47
- package/v2Containers/Email/index.js +5 -1
- package/v2Containers/EmailWrapper/components/EmailHTMLEditor.js +70 -23
- package/v2Containers/EmailWrapper/components/__tests__/EmailHTMLEditor.test.js +120 -20
- package/v2Containers/FTP/index.js +51 -2
- package/v2Containers/FTP/messages.js +4 -0
- package/v2Containers/InApp/index.js +107 -35
- package/v2Containers/InApp/tests/index.test.js +6 -17
- package/v2Containers/InappAdvance/index.js +112 -4
- package/v2Containers/InappAdvance/tests/index.test.js +0 -2
- package/v2Containers/Line/Container/Text/index.js +1 -0
- package/v2Containers/MobilePush/Create/index.js +19 -59
- package/v2Containers/MobilePush/Edit/index.js +20 -48
- package/v2Containers/MobilePushNew/index.js +32 -12
- package/v2Containers/MobilepushWrapper/index.js +1 -3
- package/v2Containers/Rcs/index.js +37 -12
- package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +1276 -1408
- package/v2Containers/Sms/Create/index.js +3 -39
- package/v2Containers/Sms/Create/messages.js +0 -4
- package/v2Containers/Sms/Edit/index.js +3 -35
- package/v2Containers/Sms/commonMethods.js +6 -3
- package/v2Containers/SmsTrai/Edit/index.js +47 -11
- package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +294 -327
- package/v2Containers/SmsWrapper/index.js +0 -2
- package/v2Containers/TemplatesV2/index.js +13 -28
- package/v2Containers/Viber/index.js +1 -0
- package/v2Containers/WebPush/Create/hooks/useTagManagement.js +3 -1
- package/v2Containers/WebPush/Create/hooks/useTagManagement.test.js +7 -0
- package/v2Containers/WebPush/Create/index.js +2 -2
- package/v2Containers/WebPush/Create/utils/validation.js +8 -17
- package/v2Containers/WebPush/Create/utils/validation.test.js +24 -44
- package/v2Containers/Whatsapp/index.js +17 -9
- package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +4872 -5246
- package/v2Containers/Zalo/index.js +11 -3
- package/v2Components/CommonTestAndPreview/AddTestCustomer.js +0 -42
- package/v2Components/CommonTestAndPreview/CustomerCreationModal.js +0 -284
- package/v2Components/CommonTestAndPreview/ExistingCustomerModal.js +0 -72
- package/v2Components/CommonTestAndPreview/tests/AddTestCustomer.test.js +0 -66
- package/v2Components/CommonTestAndPreview/tests/CommonTestAndPreview.addTestCustomer.test.js +0 -657
- package/v2Components/CommonTestAndPreview/tests/CustomValuesEditor.test.js +0 -172
- package/v2Components/CommonTestAndPreview/tests/CustomerCreationModal.test.js +0 -466
- package/v2Components/CommonTestAndPreview/tests/ExistingCustomerModal.test.js +0 -114
- package/v2Containers/Sms/tests/commonMethods.test.js +0 -122
package/v2Components/CommonTestAndPreview/tests/CommonTestAndPreview.addTestCustomer.test.js
DELETED
|
@@ -1,657 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Integration tests for Add Test Customer flow in CommonTestAndPreview
|
|
3
|
-
*
|
|
4
|
-
* Tests: lookup trigger, existing/new modals, Save, toast on failure, loading states.
|
|
5
|
-
* SendTestMessage and AddTestCustomerButton are NOT mocked so we can simulate clicks.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import React from 'react';
|
|
9
|
-
import PropTypes from 'prop-types';
|
|
10
|
-
import {
|
|
11
|
-
render, screen, waitFor, fireEvent,
|
|
12
|
-
} from '@testing-library/react';
|
|
13
|
-
import { IntlProvider } from 'react-intl';
|
|
14
|
-
import CommonTestAndPreview from '../index';
|
|
15
|
-
import { CHANNELS } from '../constants';
|
|
16
|
-
|
|
17
|
-
jest.mock('html-to-text', () => ({
|
|
18
|
-
convert: jest.fn((html) => (typeof html === 'string' ? html.replace(/<[^>]*>/g, '').trim() : '')),
|
|
19
|
-
}));
|
|
20
|
-
|
|
21
|
-
jest.mock('@capillarytech/cap-ui-library/CapNotification', () => ({
|
|
22
|
-
error: jest.fn(),
|
|
23
|
-
success: jest.fn(),
|
|
24
|
-
warning: jest.fn(),
|
|
25
|
-
info: jest.fn(),
|
|
26
|
-
}));
|
|
27
|
-
|
|
28
|
-
jest.mock('../LeftPanelContent', () => {
|
|
29
|
-
const ReactLib = require('react');
|
|
30
|
-
return {
|
|
31
|
-
__esModule: true,
|
|
32
|
-
default: function MockLeftPanelContent() {
|
|
33
|
-
return ReactLib.createElement('div', { 'data-testid': 'left-panel' }, 'Left Panel');
|
|
34
|
-
},
|
|
35
|
-
};
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
jest.mock('../CustomValuesEditor', () => {
|
|
39
|
-
const ReactLib = require('react');
|
|
40
|
-
return {
|
|
41
|
-
__esModule: true,
|
|
42
|
-
default: function MockCustomValuesEditor() {
|
|
43
|
-
return ReactLib.createElement('div', { 'data-testid': 'custom-values-editor' }, 'Custom Values Editor');
|
|
44
|
-
},
|
|
45
|
-
};
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
jest.mock('../PreviewSection', () => {
|
|
49
|
-
const ReactLib = require('react');
|
|
50
|
-
return {
|
|
51
|
-
__esModule: true,
|
|
52
|
-
default: function MockPreviewSection() {
|
|
53
|
-
return ReactLib.createElement('div', { 'data-testid': 'preview-section' }, 'Preview Section');
|
|
54
|
-
},
|
|
55
|
-
};
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
jest.mock('../../../utils/cdnTransformation', () => ({
|
|
59
|
-
getCdnUrl: jest.fn(({ url }) => `cdn_${url}`),
|
|
60
|
-
}));
|
|
61
|
-
|
|
62
|
-
// Accordion expanded so TreeSelect and Add button are in the DOM
|
|
63
|
-
jest.mock('@capillarytech/cap-ui-library/CapStepsAccordian', () => {
|
|
64
|
-
const ReactLib = require('react');
|
|
65
|
-
return function MockCapStepsAccordian({ items }) {
|
|
66
|
-
return ReactLib.createElement('div', { 'data-testid': 'cap-steps-accordian' },
|
|
67
|
-
items.map((item, index) =>
|
|
68
|
-
ReactLib.createElement('div', { key: item.key || index, 'data-testid': `accordion-item-${index}` },
|
|
69
|
-
item.header,
|
|
70
|
-
item.content)
|
|
71
|
-
)
|
|
72
|
-
);
|
|
73
|
-
};
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
// TreeSelect mock: render search input + notFoundContent so we can type and click Add without opening a real dropdown
|
|
77
|
-
jest.mock('@capillarytech/cap-ui-library/CapTreeSelect', () => {
|
|
78
|
-
const ReactLib = require('react');
|
|
79
|
-
return function MockCapTreeSelect({ searchValue, onSearch, notFoundContent, ...rest }) {
|
|
80
|
-
return ReactLib.createElement('div', { 'data-testid': 'mock-tree-select' },
|
|
81
|
-
ReactLib.createElement('input', {
|
|
82
|
-
'data-testid': 'tree-select-search',
|
|
83
|
-
'aria-label': 'Search test customers',
|
|
84
|
-
value: searchValue || '',
|
|
85
|
-
onChange: (e) => onSearch && onSearch(e.target.value),
|
|
86
|
-
}),
|
|
87
|
-
notFoundContent
|
|
88
|
-
);
|
|
89
|
-
};
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
const mockGetMembersLookup = jest.fn();
|
|
93
|
-
const mockCreateTestCustomer = jest.fn();
|
|
94
|
-
jest.mock('../../../services/api', () => ({
|
|
95
|
-
getMembersLookup: (...args) => mockGetMembersLookup(...args),
|
|
96
|
-
createTestCustomer: (...args) => mockCreateTestCustomer(...args),
|
|
97
|
-
}));
|
|
98
|
-
|
|
99
|
-
const mockMessages = {
|
|
100
|
-
'app.v2Components.TestAndPreviewSlidebox.testAndPreviewHeader': 'Preview and Test',
|
|
101
|
-
'app.v2Components.TestAndPreviewSlidebox.addTestCustomerWithValue': 'Add {searchValue} as Test Customer',
|
|
102
|
-
'app.v2Components.TestAndPreviewSlidebox.memberLookupError': 'Unable to look up customer. Please try again.',
|
|
103
|
-
'app.v2Components.TestAndPreviewSlidebox.customerCreationModalTitle': 'Add new test customer',
|
|
104
|
-
'app.v2Components.TestAndPreviewSlidebox.existingCustomerModalDescription': 'This user profile already exists in the system.',
|
|
105
|
-
'app.v2Components.TestAndPreviewSlidebox.saveButton': 'Save',
|
|
106
|
-
'app.v2Components.TestAndPreviewSlidebox.cancelButton': 'Cancel',
|
|
107
|
-
'app.v2Components.TestAndPreviewSlidebox.customerEmail': 'Email',
|
|
108
|
-
'app.v2Components.TestAndPreviewSlidebox.customerMobileNumber': 'Mobile Number',
|
|
109
|
-
'app.v2Components.TestAndPreviewSlidebox.customerID': 'Customer ID',
|
|
110
|
-
'app.v2Components.TestAndPreviewSlidebox.errorTitle': 'Error',
|
|
111
|
-
'app.v2Components.TestAndPreviewSlidebox.customerAlreadyInTestList': 'This customer is already in the test customers list.',
|
|
112
|
-
'app.v2Components.TestAndPreviewSlidebox.newTestCustomerAddedSuccess': 'New test customer added successfully!',
|
|
113
|
-
'app.v2Components.TestAndPreviewSlidebox.failedToAddTestCustomer': 'Failed to add test customer',
|
|
114
|
-
'app.v2Components.TestAndPreviewSlidebox.errorAddingTestCustomer': 'An error occurred while adding test customer',
|
|
115
|
-
};
|
|
116
|
-
|
|
117
|
-
const TestWrapper = ({ children }) => (
|
|
118
|
-
<IntlProvider locale="en" messages={mockMessages}>
|
|
119
|
-
{children}
|
|
120
|
-
</IntlProvider>
|
|
121
|
-
);
|
|
122
|
-
|
|
123
|
-
TestWrapper.propTypes = { children: PropTypes.node };
|
|
124
|
-
|
|
125
|
-
describe('CommonTestAndPreview – Add Test Customer flow', () => {
|
|
126
|
-
const mockActions = {
|
|
127
|
-
searchCustomersRequested: jest.fn(),
|
|
128
|
-
extractTagsRequested: jest.fn(),
|
|
129
|
-
updatePreviewRequested: jest.fn(),
|
|
130
|
-
sendTestMessageRequested: jest.fn(),
|
|
131
|
-
clearCustomerSearchState: jest.fn(),
|
|
132
|
-
getTestCustomersRequested: jest.fn(),
|
|
133
|
-
getTestGroupsRequested: jest.fn(),
|
|
134
|
-
createMessageMetaRequested: jest.fn(),
|
|
135
|
-
getPrefilledValuesRequested: jest.fn(),
|
|
136
|
-
clearPrefilledValues: jest.fn(),
|
|
137
|
-
clearPreviewErrors: jest.fn(),
|
|
138
|
-
addTestCustomer: jest.fn(),
|
|
139
|
-
};
|
|
140
|
-
|
|
141
|
-
const formatMessage = (msg) => (msg && typeof msg === 'object' && msg.id ? mockMessages[msg.id] : undefined) ?? msg?.defaultMessage ?? msg?.id ?? '';
|
|
142
|
-
const defaultProps = {
|
|
143
|
-
intl: { formatMessage },
|
|
144
|
-
show: true,
|
|
145
|
-
onClose: jest.fn(),
|
|
146
|
-
channel: CHANNELS.EMAIL,
|
|
147
|
-
formData: { 'template-subject': 'Test', 0: { activeTab: 'base', base: { 'template-content': '<p>Hi</p>' } } },
|
|
148
|
-
content: '',
|
|
149
|
-
config: { enableCustomerSearch: true, enableTagExtraction: true, enableTestMessage: true },
|
|
150
|
-
actions: mockActions,
|
|
151
|
-
extractedTags: [],
|
|
152
|
-
isExtractingTags: false,
|
|
153
|
-
customers: [],
|
|
154
|
-
isSearchingCustomer: false,
|
|
155
|
-
previewData: null,
|
|
156
|
-
isUpdatingPreview: false,
|
|
157
|
-
updatePreviewError: null,
|
|
158
|
-
updatePreviewErrors: [],
|
|
159
|
-
testCustomers: [],
|
|
160
|
-
isFetchingTestCustomers: false,
|
|
161
|
-
testGroups: [],
|
|
162
|
-
isFetchingTestGroups: false,
|
|
163
|
-
messageMetaConfigId: null,
|
|
164
|
-
prefilledValues: {},
|
|
165
|
-
fetchPrefilledValuesError: null,
|
|
166
|
-
fetchPrefilledValuesErrors: [],
|
|
167
|
-
isSendingTestMessage: false,
|
|
168
|
-
currentTab: 1,
|
|
169
|
-
};
|
|
170
|
-
|
|
171
|
-
beforeEach(() => {
|
|
172
|
-
jest.clearAllMocks();
|
|
173
|
-
Object.values(mockActions).forEach((fn) => fn.mockClear?.());
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
const openTreeSelectAndType = async (container, searchText) => {
|
|
177
|
-
const input = screen.getByTestId('tree-select-search');
|
|
178
|
-
fireEvent.change(input, { target: { value: searchText } });
|
|
179
|
-
await waitFor(() => {}, { timeout: 100 });
|
|
180
|
-
};
|
|
181
|
-
|
|
182
|
-
const clickAddTestCustomer = async () => {
|
|
183
|
-
const btn = await screen.findByRole('button', { name: /add.*test customer/i });
|
|
184
|
-
fireEvent.click(btn);
|
|
185
|
-
};
|
|
186
|
-
|
|
187
|
-
describe('Click Add as test customer triggers lookup', () => {
|
|
188
|
-
it('should call getMembersLookup with identifierType and identifierValue when Add is clicked', async () => {
|
|
189
|
-
mockGetMembersLookup.mockResolvedValue({ success: true, status: {}, response: { exists: false, customerDetails: [] } });
|
|
190
|
-
const { container } = render(
|
|
191
|
-
<TestWrapper>
|
|
192
|
-
<CommonTestAndPreview {...defaultProps} channel={CHANNELS.EMAIL} />
|
|
193
|
-
</TestWrapper>
|
|
194
|
-
);
|
|
195
|
-
await openTreeSelectAndType(container, 'user@example.com');
|
|
196
|
-
await waitFor(() => { expect(screen.queryByRole('button', { name: /add.*test customer/i }) || document.querySelector('.add-btn')).toBeTruthy(); }, { timeout: 2000 }).catch(() => {});
|
|
197
|
-
const addBtn = screen.queryByRole('button', { name: /add.*test customer/i }) || document.querySelector('.add-btn');
|
|
198
|
-
if (addBtn) {
|
|
199
|
-
fireEvent.click(addBtn);
|
|
200
|
-
await waitFor(() => expect(mockGetMembersLookup).toHaveBeenCalled());
|
|
201
|
-
expect(mockGetMembersLookup).toHaveBeenCalledWith('email', 'user@example.com');
|
|
202
|
-
}
|
|
203
|
-
});
|
|
204
|
-
});
|
|
205
|
-
|
|
206
|
-
describe('Lookup returns existing customer – open Existing Customer Modal', () => {
|
|
207
|
-
it('should open Existing Customer Modal with customer details when lookup returns exists=true', async () => {
|
|
208
|
-
mockGetMembersLookup.mockResolvedValue({
|
|
209
|
-
success: true,
|
|
210
|
-
status: {},
|
|
211
|
-
response: {
|
|
212
|
-
exists: true,
|
|
213
|
-
customerDetails: [{ firstName: 'John', lastName: 'Doe', userId: 'cust-123', identifiers: [{ type: 'email', value: 'user@example.com' }] }],
|
|
214
|
-
},
|
|
215
|
-
});
|
|
216
|
-
const { container } = render(
|
|
217
|
-
<TestWrapper>
|
|
218
|
-
<CommonTestAndPreview {...defaultProps} channel={CHANNELS.EMAIL} />
|
|
219
|
-
</TestWrapper>
|
|
220
|
-
);
|
|
221
|
-
await openTreeSelectAndType(container, 'user@example.com');
|
|
222
|
-
await clickAddTestCustomer();
|
|
223
|
-
await waitFor(() => expect(mockGetMembersLookup).toHaveBeenCalled());
|
|
224
|
-
await waitFor(() => {
|
|
225
|
-
expect(screen.queryByText(/this user profile already exists/i)).toBeTruthy();
|
|
226
|
-
}, { timeout: 2000 }).catch(() => {});
|
|
227
|
-
});
|
|
228
|
-
});
|
|
229
|
-
|
|
230
|
-
describe('Lookup returns no customer – open New Customer Modal', () => {
|
|
231
|
-
it('should open New Customer Modal when lookup returns exists=false', async () => {
|
|
232
|
-
mockGetMembersLookup.mockResolvedValue({ success: true, status: {}, response: { exists: false, customerDetails: [] } });
|
|
233
|
-
const { container } = render(
|
|
234
|
-
<TestWrapper>
|
|
235
|
-
<CommonTestAndPreview {...defaultProps} channel={CHANNELS.EMAIL} />
|
|
236
|
-
</TestWrapper>
|
|
237
|
-
);
|
|
238
|
-
await openTreeSelectAndType(container, 'newuser@example.com');
|
|
239
|
-
await clickAddTestCustomer();
|
|
240
|
-
await waitFor(() => expect(mockGetMembersLookup).toHaveBeenCalled());
|
|
241
|
-
await waitFor(() => {
|
|
242
|
-
expect(screen.queryByText(/add new test customer/i)).toBeTruthy();
|
|
243
|
-
}, { timeout: 2000 }).catch(() => {});
|
|
244
|
-
});
|
|
245
|
-
});
|
|
246
|
-
|
|
247
|
-
describe('Lookup failure shows toast, no modal', () => {
|
|
248
|
-
it('should show error toast when getMembersLookup fails', async () => {
|
|
249
|
-
const CapNotification = require('@capillarytech/cap-ui-library/CapNotification');
|
|
250
|
-
mockGetMembersLookup.mockRejectedValue(new Error('Network error'));
|
|
251
|
-
const { container } = render(
|
|
252
|
-
<TestWrapper>
|
|
253
|
-
<CommonTestAndPreview {...defaultProps} channel={CHANNELS.EMAIL} />
|
|
254
|
-
</TestWrapper>
|
|
255
|
-
);
|
|
256
|
-
await openTreeSelectAndType(container, 'user@example.com');
|
|
257
|
-
await clickAddTestCustomer();
|
|
258
|
-
await waitFor(() => expect(mockGetMembersLookup).toHaveBeenCalled());
|
|
259
|
-
await waitFor(() => expect(CapNotification.error).toHaveBeenCalled());
|
|
260
|
-
// Existing-customer modal must not open on lookup failure
|
|
261
|
-
expect(screen.queryByText(/this user profile already exists/i)).toBeFalsy();
|
|
262
|
-
});
|
|
263
|
-
});
|
|
264
|
-
|
|
265
|
-
describe('Already in test list (before API)', () => {
|
|
266
|
-
it('should show success and not call lookup when typed email is already in testCustomers', async () => {
|
|
267
|
-
const CapNotification = require('@capillarytech/cap-ui-library/CapNotification');
|
|
268
|
-
const testCustomersWithEmail = [
|
|
269
|
-
{ email: 'already@test.com', customerId: 'cust-1', userId: 'cust-1', name: 'Already', mobile: '' },
|
|
270
|
-
];
|
|
271
|
-
const { container } = render(
|
|
272
|
-
<TestWrapper>
|
|
273
|
-
<CommonTestAndPreview
|
|
274
|
-
{...defaultProps}
|
|
275
|
-
channel={CHANNELS.EMAIL}
|
|
276
|
-
testCustomers={testCustomersWithEmail}
|
|
277
|
-
/>
|
|
278
|
-
</TestWrapper>
|
|
279
|
-
);
|
|
280
|
-
await openTreeSelectAndType(container, 'already@test.com');
|
|
281
|
-
await clickAddTestCustomer();
|
|
282
|
-
await waitFor(() => expect(CapNotification.success).toHaveBeenCalledWith(expect.objectContaining({
|
|
283
|
-
message: 'This customer is already in the test customers list.',
|
|
284
|
-
})));
|
|
285
|
-
expect(mockGetMembersLookup).not.toHaveBeenCalled();
|
|
286
|
-
});
|
|
287
|
-
|
|
288
|
-
it('should show success and not call lookup when typed mobile is already in testCustomers (SMS)', async () => {
|
|
289
|
-
const CapNotification = require('@capillarytech/cap-ui-library/CapNotification');
|
|
290
|
-
const testCustomersWithMobile = [
|
|
291
|
-
{ mobile: '9876543210', customerId: 'cust-2', userId: 'cust-2', name: 'Mobile', email: '' },
|
|
292
|
-
];
|
|
293
|
-
const { container } = render(
|
|
294
|
-
<TestWrapper>
|
|
295
|
-
<CommonTestAndPreview
|
|
296
|
-
{...defaultProps}
|
|
297
|
-
channel={CHANNELS.SMS}
|
|
298
|
-
testCustomers={testCustomersWithMobile}
|
|
299
|
-
/>
|
|
300
|
-
</TestWrapper>
|
|
301
|
-
);
|
|
302
|
-
await openTreeSelectAndType(container, '9876543210');
|
|
303
|
-
await clickAddTestCustomer();
|
|
304
|
-
await waitFor(() => expect(CapNotification.success).toHaveBeenCalledWith(expect.objectContaining({
|
|
305
|
-
message: 'This customer is already in the test customers list.',
|
|
306
|
-
})));
|
|
307
|
-
expect(mockGetMembersLookup).not.toHaveBeenCalled();
|
|
308
|
-
});
|
|
309
|
-
});
|
|
310
|
-
|
|
311
|
-
describe('Already in test list by customerId (after lookup)', () => {
|
|
312
|
-
it('should show already-in-list success and not open Existing Customer modal when lookup customer is already in test list', async () => {
|
|
313
|
-
const CapNotification = require('@capillarytech/cap-ui-library/CapNotification');
|
|
314
|
-
// Customer in list has different email so we don't match "before API" branch; lookup returns same customerId
|
|
315
|
-
const testCustomersWithCust = [
|
|
316
|
-
{ email: 'other@test.com', customerId: 'cust-same', userId: 'cust-same', name: 'Same', mobile: '' },
|
|
317
|
-
];
|
|
318
|
-
mockGetMembersLookup.mockResolvedValue({
|
|
319
|
-
success: true,
|
|
320
|
-
status: {},
|
|
321
|
-
response: {
|
|
322
|
-
exists: true,
|
|
323
|
-
customerDetails: [{
|
|
324
|
-
firstName: 'Same',
|
|
325
|
-
lastName: 'User',
|
|
326
|
-
userId: 'cust-same',
|
|
327
|
-
identifiers: [{ type: 'email', value: 'lookup@test.com' }],
|
|
328
|
-
}],
|
|
329
|
-
},
|
|
330
|
-
});
|
|
331
|
-
const { container } = render(
|
|
332
|
-
<TestWrapper>
|
|
333
|
-
<CommonTestAndPreview
|
|
334
|
-
{...defaultProps}
|
|
335
|
-
channel={CHANNELS.EMAIL}
|
|
336
|
-
testCustomers={testCustomersWithCust}
|
|
337
|
-
/>
|
|
338
|
-
</TestWrapper>
|
|
339
|
-
);
|
|
340
|
-
await openTreeSelectAndType(container, 'lookup@test.com');
|
|
341
|
-
await clickAddTestCustomer();
|
|
342
|
-
await waitFor(() => expect(mockGetMembersLookup).toHaveBeenCalled());
|
|
343
|
-
await waitFor(() => expect(CapNotification.success).toHaveBeenCalledWith(expect.objectContaining({
|
|
344
|
-
message: 'This customer is already in the test customers list.',
|
|
345
|
-
})));
|
|
346
|
-
expect(screen.queryByText(/this user profile already exists/i)).toBeFalsy();
|
|
347
|
-
});
|
|
348
|
-
});
|
|
349
|
-
|
|
350
|
-
describe('Lookup throws (catch block)', () => {
|
|
351
|
-
it('should show memberLookupError and not open any modal when getMembersLookup rejects', async () => {
|
|
352
|
-
const CapNotification = require('@capillarytech/cap-ui-library/CapNotification');
|
|
353
|
-
mockGetMembersLookup.mockRejectedValue(new Error('Network error'));
|
|
354
|
-
const { container } = render(
|
|
355
|
-
<TestWrapper>
|
|
356
|
-
<CommonTestAndPreview {...defaultProps} channel={CHANNELS.EMAIL} />
|
|
357
|
-
</TestWrapper>
|
|
358
|
-
);
|
|
359
|
-
await openTreeSelectAndType(container, 'user@example.com');
|
|
360
|
-
await clickAddTestCustomer();
|
|
361
|
-
await waitFor(() => expect(mockGetMembersLookup).toHaveBeenCalled());
|
|
362
|
-
await waitFor(() => expect(CapNotification.error).toHaveBeenCalledWith(expect.objectContaining({
|
|
363
|
-
message: 'Unable to look up customer. Please try again.',
|
|
364
|
-
description: 'Unable to look up customer. Please try again.',
|
|
365
|
-
})));
|
|
366
|
-
expect(screen.queryByText(/add new test customer/i)).toBeFalsy();
|
|
367
|
-
});
|
|
368
|
-
});
|
|
369
|
-
|
|
370
|
-
describe('Existing Customer Modal – Save calls createTestCustomer', () => {
|
|
371
|
-
it('should call createTestCustomer when Save is clicked in Existing Customer Modal', async () => {
|
|
372
|
-
mockGetMembersLookup.mockResolvedValue({
|
|
373
|
-
success: true,
|
|
374
|
-
status: {},
|
|
375
|
-
response: {
|
|
376
|
-
exists: true,
|
|
377
|
-
customerDetails: [{ firstName: 'J', lastName: 'D', userId: 'cust-456', identifiers: [{ type: 'email', value: 'existing@example.com' }] }],
|
|
378
|
-
},
|
|
379
|
-
});
|
|
380
|
-
mockCreateTestCustomer.mockResolvedValue({ success: true });
|
|
381
|
-
const { container } = render(
|
|
382
|
-
<TestWrapper>
|
|
383
|
-
<CommonTestAndPreview {...defaultProps} channel={CHANNELS.EMAIL} />
|
|
384
|
-
</TestWrapper>
|
|
385
|
-
);
|
|
386
|
-
await openTreeSelectAndType(container, 'existing@example.com');
|
|
387
|
-
await clickAddTestCustomer();
|
|
388
|
-
await waitFor(() => expect(mockGetMembersLookup).toHaveBeenCalled());
|
|
389
|
-
await waitFor(() => expect(screen.queryByText(/save/i)).toBeTruthy(), { timeout: 2000 });
|
|
390
|
-
const saveBtn = screen.queryByRole('button', { name: /save/i });
|
|
391
|
-
if (saveBtn) {
|
|
392
|
-
fireEvent.click(saveBtn);
|
|
393
|
-
await waitFor(() => expect(mockCreateTestCustomer).toHaveBeenCalled());
|
|
394
|
-
expect(mockCreateTestCustomer).toHaveBeenCalledWith(expect.objectContaining({ customerId: 'cust-456' }));
|
|
395
|
-
}
|
|
396
|
-
});
|
|
397
|
-
});
|
|
398
|
-
|
|
399
|
-
describe('Existing Customer Modal – Save success closes modal and shows snackbar', () => {
|
|
400
|
-
it('should close modal, show success snackbar, and add customer to test list when createTestCustomer succeeds', async () => {
|
|
401
|
-
const CapNotification = require('@capillarytech/cap-ui-library/CapNotification');
|
|
402
|
-
mockGetMembersLookup.mockResolvedValue({
|
|
403
|
-
success: true,
|
|
404
|
-
status: {},
|
|
405
|
-
response: {
|
|
406
|
-
exists: true,
|
|
407
|
-
customerDetails: [{ firstName: 'A', lastName: 'B', userId: 'cust-789', identifiers: [{ type: 'email', value: 'success@example.com' }] }],
|
|
408
|
-
},
|
|
409
|
-
});
|
|
410
|
-
mockCreateTestCustomer.mockResolvedValue({ success: true });
|
|
411
|
-
const { container } = render(
|
|
412
|
-
<TestWrapper>
|
|
413
|
-
<CommonTestAndPreview {...defaultProps} channel={CHANNELS.EMAIL} />
|
|
414
|
-
</TestWrapper>
|
|
415
|
-
);
|
|
416
|
-
await openTreeSelectAndType(container, 'success@example.com');
|
|
417
|
-
await clickAddTestCustomer();
|
|
418
|
-
await waitFor(() => expect(mockGetMembersLookup).toHaveBeenCalled());
|
|
419
|
-
const saveBtn = await screen.findByRole('button', { name: /save/i }).catch(() => null);
|
|
420
|
-
if (saveBtn) {
|
|
421
|
-
fireEvent.click(saveBtn);
|
|
422
|
-
await waitFor(() => expect(mockCreateTestCustomer).toHaveBeenCalled());
|
|
423
|
-
await waitFor(() => expect(CapNotification.success).toHaveBeenCalled());
|
|
424
|
-
await waitFor(() => expect(mockActions.addTestCustomer).toHaveBeenCalledWith({
|
|
425
|
-
userId: 'cust-789',
|
|
426
|
-
customerId: 'cust-789',
|
|
427
|
-
name: 'A B',
|
|
428
|
-
email: 'success@example.com',
|
|
429
|
-
mobile: '',
|
|
430
|
-
}));
|
|
431
|
-
}
|
|
432
|
-
});
|
|
433
|
-
});
|
|
434
|
-
|
|
435
|
-
describe('Member creation failure shows toast, modal stays open', () => {
|
|
436
|
-
it('should show error toast and keep modal open when createTestCustomer fails', async () => {
|
|
437
|
-
const CapNotification = require('@capillarytech/cap-ui-library/CapNotification');
|
|
438
|
-
mockGetMembersLookup.mockResolvedValue({
|
|
439
|
-
success: true,
|
|
440
|
-
status: {},
|
|
441
|
-
response: {
|
|
442
|
-
exists: true,
|
|
443
|
-
customerDetails: [{ firstName: 'F', lastName: 'F', userId: 'cust-fail', identifiers: [{ type: 'email', value: 'fail@example.com' }] }],
|
|
444
|
-
},
|
|
445
|
-
});
|
|
446
|
-
mockCreateTestCustomer.mockRejectedValue(new Error('Create failed'));
|
|
447
|
-
const { container } = render(
|
|
448
|
-
<TestWrapper>
|
|
449
|
-
<CommonTestAndPreview {...defaultProps} channel={CHANNELS.EMAIL} />
|
|
450
|
-
</TestWrapper>
|
|
451
|
-
);
|
|
452
|
-
await openTreeSelectAndType(container, 'fail@example.com');
|
|
453
|
-
await clickAddTestCustomer();
|
|
454
|
-
await waitFor(() => expect(mockGetMembersLookup).toHaveBeenCalled());
|
|
455
|
-
const saveBtn = await screen.findByRole('button', { name: /save/i }).catch(() => null);
|
|
456
|
-
if (saveBtn) {
|
|
457
|
-
fireEvent.click(saveBtn);
|
|
458
|
-
await waitFor(() => expect(CapNotification.error).toHaveBeenCalled());
|
|
459
|
-
}
|
|
460
|
-
});
|
|
461
|
-
});
|
|
462
|
-
|
|
463
|
-
describe('Loading states handled correctly', () => {
|
|
464
|
-
it('should show loading state during lookup and clear after response', async () => {
|
|
465
|
-
let resolveLookup;
|
|
466
|
-
mockGetMembersLookup.mockImplementation(() => new Promise((r) => { resolveLookup = r; }));
|
|
467
|
-
const { container } = render(
|
|
468
|
-
<TestWrapper>
|
|
469
|
-
<CommonTestAndPreview {...defaultProps} channel={CHANNELS.EMAIL} />
|
|
470
|
-
</TestWrapper>
|
|
471
|
-
);
|
|
472
|
-
await openTreeSelectAndType(container, 'user@example.com');
|
|
473
|
-
await clickAddTestCustomer();
|
|
474
|
-
await waitFor(() => expect(mockGetMembersLookup).toHaveBeenCalled());
|
|
475
|
-
if (resolveLookup) resolveLookup({ success: true, status: {}, response: { exists: false, customerDetails: [] } });
|
|
476
|
-
await waitFor(() => expect(mockGetMembersLookup).toHaveBeenCalled());
|
|
477
|
-
});
|
|
478
|
-
});
|
|
479
|
-
|
|
480
|
-
describe('SMS channel – phone sent to API', () => {
|
|
481
|
-
it('should call getMembersLookup with mobile and digits when channel is SMS', async () => {
|
|
482
|
-
mockGetMembersLookup.mockResolvedValue({ success: true, status: {}, response: { exists: false, customerDetails: [] } });
|
|
483
|
-
const { container } = render(
|
|
484
|
-
<TestWrapper>
|
|
485
|
-
<CommonTestAndPreview {...defaultProps} channel={CHANNELS.SMS} />
|
|
486
|
-
</TestWrapper>
|
|
487
|
-
);
|
|
488
|
-
await openTreeSelectAndType(container, '9123456789');
|
|
489
|
-
await clickAddTestCustomer();
|
|
490
|
-
await waitFor(() => expect(mockGetMembersLookup).toHaveBeenCalled()).catch(() => {});
|
|
491
|
-
if (mockGetMembersLookup.mock.calls.length) {
|
|
492
|
-
expect(mockGetMembersLookup).toHaveBeenCalledWith('mobile', '9123456789');
|
|
493
|
-
}
|
|
494
|
-
});
|
|
495
|
-
});
|
|
496
|
-
|
|
497
|
-
describe('New Customer Modal – Save success', () => {
|
|
498
|
-
it('should call createTestCustomer with customer payload, show success, and add new customer to test list when Save succeeds and API returns customerId', async () => {
|
|
499
|
-
const CapNotification = require('@capillarytech/cap-ui-library/CapNotification');
|
|
500
|
-
mockGetMembersLookup.mockResolvedValue({ success: true, status: {}, response: { exists: false, customerDetails: [] } });
|
|
501
|
-
mockCreateTestCustomer.mockResolvedValue({ success: true, customerId: 'newuser@example.com' });
|
|
502
|
-
const { container } = render(
|
|
503
|
-
<TestWrapper>
|
|
504
|
-
<CommonTestAndPreview {...defaultProps} channel={CHANNELS.EMAIL} />
|
|
505
|
-
</TestWrapper>
|
|
506
|
-
);
|
|
507
|
-
await openTreeSelectAndType(container, 'newuser@example.com');
|
|
508
|
-
await clickAddTestCustomer();
|
|
509
|
-
await waitFor(() => expect(mockGetMembersLookup).toHaveBeenCalled());
|
|
510
|
-
await waitFor(() => expect(screen.queryByText(/add new test customer/i)).toBeTruthy(), { timeout: 2000 });
|
|
511
|
-
const saveBtn = screen.queryByRole('button', { name: /save/i });
|
|
512
|
-
if (saveBtn) {
|
|
513
|
-
fireEvent.click(saveBtn);
|
|
514
|
-
await waitFor(() => expect(mockCreateTestCustomer).toHaveBeenCalled());
|
|
515
|
-
expect(mockCreateTestCustomer).toHaveBeenCalledWith(expect.objectContaining({
|
|
516
|
-
customer: expect.objectContaining({
|
|
517
|
-
email: 'newuser@example.com',
|
|
518
|
-
firstName: '',
|
|
519
|
-
mobile: '',
|
|
520
|
-
}),
|
|
521
|
-
}));
|
|
522
|
-
await waitFor(() => expect(CapNotification.success).toHaveBeenCalled());
|
|
523
|
-
// When API returns customerId, new customer is added to dropdown and selection
|
|
524
|
-
await waitFor(() => expect(mockActions.addTestCustomer).toHaveBeenCalledWith(expect.objectContaining({
|
|
525
|
-
userId: 'newuser@example.com',
|
|
526
|
-
customerId: 'newuser@example.com',
|
|
527
|
-
name: '',
|
|
528
|
-
email: 'newuser@example.com',
|
|
529
|
-
mobile: '',
|
|
530
|
-
})));
|
|
531
|
-
}
|
|
532
|
-
});
|
|
533
|
-
});
|
|
534
|
-
|
|
535
|
-
describe('New Customer Modal – Save API returns success: false', () => {
|
|
536
|
-
it('should show error toast when createTestCustomer returns success: false', async () => {
|
|
537
|
-
const CapNotification = require('@capillarytech/cap-ui-library/CapNotification');
|
|
538
|
-
mockGetMembersLookup.mockResolvedValue({ success: true, status: {}, response: { exists: false, customerDetails: [] } });
|
|
539
|
-
mockCreateTestCustomer.mockResolvedValue({ success: false, message: 'Server error' });
|
|
540
|
-
const { container } = render(
|
|
541
|
-
<TestWrapper>
|
|
542
|
-
<CommonTestAndPreview {...defaultProps} channel={CHANNELS.EMAIL} />
|
|
543
|
-
</TestWrapper>
|
|
544
|
-
);
|
|
545
|
-
await openTreeSelectAndType(container, 'newuser@example.com');
|
|
546
|
-
await clickAddTestCustomer();
|
|
547
|
-
await waitFor(() => expect(mockGetMembersLookup).toHaveBeenCalled());
|
|
548
|
-
const saveBtn = await screen.findByRole('button', { name: /save/i }).catch(() => null);
|
|
549
|
-
if (saveBtn) {
|
|
550
|
-
fireEvent.click(saveBtn);
|
|
551
|
-
await waitFor(() => expect(mockCreateTestCustomer).toHaveBeenCalled());
|
|
552
|
-
await waitFor(() => expect(CapNotification.error).toHaveBeenCalledWith(expect.objectContaining({
|
|
553
|
-
message: 'Error',
|
|
554
|
-
description: 'Server error',
|
|
555
|
-
})));
|
|
556
|
-
}
|
|
557
|
-
});
|
|
558
|
-
});
|
|
559
|
-
|
|
560
|
-
describe('Lookup API error (success: false or status.isError)', () => {
|
|
561
|
-
it('should show error with API message and not open any modal when lookup returns success: false (e.g. Merged customer found)', async () => {
|
|
562
|
-
const CapNotification = require('@capillarytech/cap-ui-library/CapNotification');
|
|
563
|
-
mockGetMembersLookup.mockResolvedValue({
|
|
564
|
-
success: false,
|
|
565
|
-
status: { isError: true, code: 400, message: 'Merged customer found' },
|
|
566
|
-
message: 'Merged customer found',
|
|
567
|
-
});
|
|
568
|
-
const { container } = render(
|
|
569
|
-
<TestWrapper>
|
|
570
|
-
<CommonTestAndPreview {...defaultProps} channel={CHANNELS.EMAIL} />
|
|
571
|
-
</TestWrapper>
|
|
572
|
-
);
|
|
573
|
-
await openTreeSelectAndType(container, 'user@example.com');
|
|
574
|
-
await clickAddTestCustomer();
|
|
575
|
-
await waitFor(() => expect(mockGetMembersLookup).toHaveBeenCalled());
|
|
576
|
-
await waitFor(() => expect(CapNotification.error).toHaveBeenCalledWith(expect.objectContaining({
|
|
577
|
-
message: 'Unable to look up customer. Please try again.',
|
|
578
|
-
description: 'Merged customer found',
|
|
579
|
-
})));
|
|
580
|
-
expect(screen.queryByText(/add new test customer/i)).toBeFalsy();
|
|
581
|
-
expect(screen.queryByText(/this user profile already exists/i)).toBeFalsy();
|
|
582
|
-
});
|
|
583
|
-
|
|
584
|
-
it('should show error and not open any modal when response has status.isError', async () => {
|
|
585
|
-
const CapNotification = require('@capillarytech/cap-ui-library/CapNotification');
|
|
586
|
-
mockGetMembersLookup.mockResolvedValue({
|
|
587
|
-
success: true,
|
|
588
|
-
status: { isError: true, message: 'Server error' },
|
|
589
|
-
response: { exists: true, customerDetails: [{ userId: 'x', identifiers: [] }] },
|
|
590
|
-
});
|
|
591
|
-
const { container } = render(
|
|
592
|
-
<TestWrapper>
|
|
593
|
-
<CommonTestAndPreview {...defaultProps} channel={CHANNELS.EMAIL} />
|
|
594
|
-
</TestWrapper>
|
|
595
|
-
);
|
|
596
|
-
await openTreeSelectAndType(container, 'user@example.com');
|
|
597
|
-
await clickAddTestCustomer();
|
|
598
|
-
await waitFor(() => expect(mockGetMembersLookup).toHaveBeenCalled());
|
|
599
|
-
await waitFor(() => expect(CapNotification.error).toHaveBeenCalledWith(expect.objectContaining({
|
|
600
|
-
message: 'Unable to look up customer. Please try again.',
|
|
601
|
-
description: 'Server error',
|
|
602
|
-
})));
|
|
603
|
-
expect(screen.queryByText(/add new test customer/i)).toBeFalsy();
|
|
604
|
-
expect(screen.queryByText(/this user profile already exists/i)).toBeFalsy();
|
|
605
|
-
});
|
|
606
|
-
|
|
607
|
-
it('should show fallback error message when lookup fails with no message', async () => {
|
|
608
|
-
const CapNotification = require('@capillarytech/cap-ui-library/CapNotification');
|
|
609
|
-
mockGetMembersLookup.mockResolvedValue({
|
|
610
|
-
success: false,
|
|
611
|
-
status: { isError: true },
|
|
612
|
-
response: {},
|
|
613
|
-
});
|
|
614
|
-
const { container } = render(
|
|
615
|
-
<TestWrapper>
|
|
616
|
-
<CommonTestAndPreview {...defaultProps} channel={CHANNELS.EMAIL} />
|
|
617
|
-
</TestWrapper>
|
|
618
|
-
);
|
|
619
|
-
await openTreeSelectAndType(container, 'user@example.com');
|
|
620
|
-
await clickAddTestCustomer();
|
|
621
|
-
await waitFor(() => expect(CapNotification.error).toHaveBeenCalledWith(expect.objectContaining({
|
|
622
|
-
message: 'Unable to look up customer. Please try again.',
|
|
623
|
-
description: 'Unable to look up customer. Please try again.',
|
|
624
|
-
})));
|
|
625
|
-
});
|
|
626
|
-
});
|
|
627
|
-
|
|
628
|
-
describe('Existing customer with commChannels maps to customerData', () => {
|
|
629
|
-
it('should map customerDetails with commChannels to customerData', async () => {
|
|
630
|
-
mockGetMembersLookup.mockResolvedValue({
|
|
631
|
-
success: true,
|
|
632
|
-
status: {},
|
|
633
|
-
response: {
|
|
634
|
-
exists: true,
|
|
635
|
-
customerDetails: [{
|
|
636
|
-
firstName: 'Jane',
|
|
637
|
-
lastName: 'Doe',
|
|
638
|
-
userId: 'cust-comm',
|
|
639
|
-
commChannels: [{ type: 'email', value: 'jane@example.com' }],
|
|
640
|
-
}],
|
|
641
|
-
},
|
|
642
|
-
});
|
|
643
|
-
const { container } = render(
|
|
644
|
-
<TestWrapper>
|
|
645
|
-
<CommonTestAndPreview {...defaultProps} channel={CHANNELS.EMAIL} />
|
|
646
|
-
</TestWrapper>
|
|
647
|
-
);
|
|
648
|
-
await openTreeSelectAndType(container, 'jane@example.com');
|
|
649
|
-
await clickAddTestCustomer();
|
|
650
|
-
await waitFor(() => expect(mockGetMembersLookup).toHaveBeenCalled());
|
|
651
|
-
await waitFor(() => {
|
|
652
|
-
expect(screen.queryByText(/jane@example.com/i)).toBeTruthy();
|
|
653
|
-
expect(screen.queryByText(/Jane Doe/i)).toBeTruthy();
|
|
654
|
-
}, { timeout: 2000 }).catch(() => {});
|
|
655
|
-
});
|
|
656
|
-
});
|
|
657
|
-
});
|