@capillarytech/creatives-library 8.0.327 → 8.0.329
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/tagValidations.js +2 -3
- package/utils/tests/commonUtil.test.js +169 -0
- package/utils/tests/tagValidations.test.js +1 -35
- package/v2Components/CapTagList/index.js +22 -14
- package/v2Components/CapTagList/style.scss +0 -48
- package/v2Components/CapTagListWithInput/index.js +0 -4
- package/v2Components/CapWhatsappCTA/index.js +0 -2
- package/v2Components/CommonTestAndPreview/AddTestCustomer.js +42 -0
- package/v2Components/CommonTestAndPreview/CustomerCreationModal.js +155 -0
- package/v2Components/CommonTestAndPreview/ExistingCustomerModal.js +93 -0
- package/v2Components/CommonTestAndPreview/SendTestMessage.js +79 -51
- package/v2Components/CommonTestAndPreview/_commonTestAndPreview.scss +134 -34
- package/v2Components/CommonTestAndPreview/actions.js +10 -0
- package/v2Components/CommonTestAndPreview/constants.js +15 -1
- package/v2Components/CommonTestAndPreview/index.js +315 -15
- package/v2Components/CommonTestAndPreview/messages.js +106 -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 +648 -0
- package/v2Components/CommonTestAndPreview/tests/CustomValuesEditor.test.js +24 -0
- package/v2Components/CommonTestAndPreview/tests/CustomerCreationModal.test.js +174 -0
- package/v2Components/CommonTestAndPreview/tests/ExistingCustomerModal.test.js +114 -0
- package/v2Components/CommonTestAndPreview/tests/SendTestMessage.test.js +52 -0
- package/v2Components/CommonTestAndPreview/tests/constants.test.js +31 -1
- package/v2Components/CommonTestAndPreview/tests/index.test.js +36 -0
- package/v2Components/CommonTestAndPreview/tests/reducer.test.js +71 -0
- package/v2Components/CommonTestAndPreview/tests/selectors.test.js +17 -0
- package/v2Components/FormBuilder/index.js +0 -7
- package/v2Components/HtmlEditor/HTMLEditor.js +1 -6
- package/v2Components/HtmlEditor/__tests__/HTMLEditor.apiErrors.test.js +0 -1
- package/v2Components/HtmlEditor/__tests__/HTMLEditor.test.js +2 -927
- package/v2Components/HtmlEditor/components/CodeEditorPane/index.js +0 -3
- package/v2Containers/BeeEditor/index.js +0 -3
- package/v2Containers/CreativesContainer/SlideBoxContent.js +1 -28
- package/v2Containers/CreativesContainer/index.js +0 -3
- package/v2Containers/Email/index.js +0 -1
- package/v2Containers/EmailWrapper/components/EmailHTMLEditor.js +1 -7
- package/v2Containers/EmailWrapper/components/EmailWrapperView.js +0 -3
- package/v2Containers/EmailWrapper/components/__tests__/EmailHTMLEditor.test.js +2 -20
- package/v2Containers/EmailWrapper/components/__tests__/EmailWrapperView.test.js +1 -16
- package/v2Containers/EmailWrapper/hooks/useEmailWrapper.js +0 -3
- package/v2Containers/EmailWrapper/index.js +0 -4
- package/v2Containers/EmailWrapper/tests/useEmailWrapper.edgeCases.test.js +0 -1
- package/v2Containers/EmailWrapper/tests/useEmailWrapper.test.js +0 -9
- package/v2Containers/InAppWrapper/hooks/__tests__/useInAppWrapper.test.js +0 -19
- package/v2Containers/InAppWrapper/hooks/useInAppWrapper.js +0 -3
- package/v2Containers/InAppWrapper/index.js +0 -3
- package/v2Containers/MobilePush/Create/index.js +0 -2
- package/v2Containers/MobilePush/Edit/index.js +0 -2
- package/v2Containers/MobilepushWrapper/index.js +1 -3
- package/v2Containers/Rcs/index.js +0 -1
- package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +1886 -1754
- package/v2Containers/Sms/Create/index.js +0 -2
- package/v2Containers/Sms/Edit/index.js +0 -2
- package/v2Containers/SmsTrai/Edit/index.js +0 -2
- package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +351 -318
- package/v2Containers/SmsWrapper/index.js +0 -2
- package/v2Containers/TagList/index.js +2 -41
- package/v2Containers/TagList/messages.js +0 -4
- package/v2Containers/TagList/tests/TagList.test.js +20 -122
- package/v2Containers/TagList/tests/mockdata.js +0 -17
- package/v2Containers/Viber/index.js +0 -5
- package/v2Containers/WebPush/Create/hooks/useTagManagement.js +2 -0
- package/v2Containers/WebPush/Create/index.js +1 -9
- package/v2Containers/Whatsapp/index.js +0 -5
- package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +5586 -5232
- package/v2Containers/Zalo/index.js +0 -2
- package/v2Components/CapTagListWithInput/__tests__/CapTagListWithInput.test.js +0 -63
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for CustomerCreationModal
|
|
3
|
+
*
|
|
4
|
+
* Email/mobile are read-only; lookup runs in parent (index.js) before the modal opens.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import React from 'react';
|
|
8
|
+
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
|
|
9
|
+
import { IntlProvider } from 'react-intl';
|
|
10
|
+
import PropTypes from 'prop-types';
|
|
11
|
+
import CustomerCreationModal from '../CustomerCreationModal';
|
|
12
|
+
import { CHANNELS, CUSTOMER_MODAL_NEW } from '../constants';
|
|
13
|
+
|
|
14
|
+
const mockMessages = {
|
|
15
|
+
'app.v2Components.TestAndPreviewSlidebox.customerCreationModalTitle': 'Add new test customer',
|
|
16
|
+
'app.v2Components.TestAndPreviewSlidebox.customerCreationModalDescription': 'This customer profile will be available for testing.',
|
|
17
|
+
'app.v2Components.TestAndPreviewSlidebox.customerName': 'Name',
|
|
18
|
+
'app.v2Components.TestAndPreviewSlidebox.customerNameOptional': '(Optional)',
|
|
19
|
+
'app.v2Components.TestAndPreviewSlidebox.customerNamePlaceholder': 'Enter the name',
|
|
20
|
+
'app.v2Components.TestAndPreviewSlidebox.customerEmailPlaceholder': 'Enter the Email',
|
|
21
|
+
'app.v2Components.TestAndPreviewSlidebox.customerMobilePlaceholder': 'Enter the Mobile Number',
|
|
22
|
+
'app.v2Components.TestAndPreviewSlidebox.saveButton': 'Save',
|
|
23
|
+
'app.v2Components.TestAndPreviewSlidebox.cancelButton': 'Cancel',
|
|
24
|
+
'app.v2Components.TestAndPreviewSlidebox.customerEmail': 'Email',
|
|
25
|
+
'app.v2Components.TestAndPreviewSlidebox.customerMobileNumber': 'Mobile Number',
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const TestWrapper = ({ children }) => (
|
|
29
|
+
<IntlProvider locale="en" messages={mockMessages}>
|
|
30
|
+
{children}
|
|
31
|
+
</IntlProvider>
|
|
32
|
+
);
|
|
33
|
+
TestWrapper.propTypes = { children: PropTypes.node };
|
|
34
|
+
|
|
35
|
+
describe('CustomerCreationModal', () => {
|
|
36
|
+
const defaultProps = {
|
|
37
|
+
customerModal: [true, CUSTOMER_MODAL_NEW],
|
|
38
|
+
onCloseCustomerModal: jest.fn(),
|
|
39
|
+
channel: CHANNELS.EMAIL,
|
|
40
|
+
customerData: { name: '', email: '', mobile: '' },
|
|
41
|
+
setCustomerData: jest.fn(),
|
|
42
|
+
onSave: jest.fn(),
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
beforeEach(() => {
|
|
46
|
+
jest.clearAllMocks();
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('renders modal with title and form fields when visible (EMAIL)', () => {
|
|
50
|
+
render(
|
|
51
|
+
<TestWrapper>
|
|
52
|
+
<CustomerCreationModal {...defaultProps} channel={CHANNELS.EMAIL} />
|
|
53
|
+
</TestWrapper>
|
|
54
|
+
);
|
|
55
|
+
expect(screen.getByText(/add new test customer/i)).toBeTruthy();
|
|
56
|
+
expect(screen.getByPlaceholderText(/enter the name/i)).toBeTruthy();
|
|
57
|
+
expect(screen.getByPlaceholderText(/enter the email/i)).toBeTruthy();
|
|
58
|
+
expect(screen.queryByPlaceholderText(/enter the mobile number/i)).toBeNull();
|
|
59
|
+
expect(screen.getByRole('button', { name: /save/i })).toBeTruthy();
|
|
60
|
+
expect(screen.getByRole('button', { name: /cancel/i })).toBeTruthy();
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('renders mobile field when channel is SMS', () => {
|
|
64
|
+
render(
|
|
65
|
+
<TestWrapper>
|
|
66
|
+
<CustomerCreationModal
|
|
67
|
+
{...defaultProps}
|
|
68
|
+
channel={CHANNELS.SMS}
|
|
69
|
+
customerData={{ name: '', email: '', mobile: '' }}
|
|
70
|
+
/>
|
|
71
|
+
</TestWrapper>
|
|
72
|
+
);
|
|
73
|
+
expect(screen.getByPlaceholderText(/enter the mobile number/i)).toBeTruthy();
|
|
74
|
+
expect(screen.queryByPlaceholderText(/enter the email/i)).toBeNull();
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('calls onCloseCustomerModal when Cancel is clicked', () => {
|
|
78
|
+
render(
|
|
79
|
+
<TestWrapper>
|
|
80
|
+
<CustomerCreationModal {...defaultProps} />
|
|
81
|
+
</TestWrapper>
|
|
82
|
+
);
|
|
83
|
+
fireEvent.click(screen.getByRole('button', { name: /cancel/i }));
|
|
84
|
+
expect(defaultProps.onCloseCustomerModal).toHaveBeenCalled();
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('calls setCustomerData when name input changes', () => {
|
|
88
|
+
render(
|
|
89
|
+
<TestWrapper>
|
|
90
|
+
<CustomerCreationModal {...defaultProps} />
|
|
91
|
+
</TestWrapper>
|
|
92
|
+
);
|
|
93
|
+
fireEvent.change(screen.getByPlaceholderText(/enter the name/i), { target: { value: 'John' } });
|
|
94
|
+
expect(defaultProps.setCustomerData).toHaveBeenCalledWith(expect.objectContaining({ name: 'John' }));
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it('calls onSave when Save is clicked with empty validation object', async () => {
|
|
98
|
+
render(
|
|
99
|
+
<TestWrapper>
|
|
100
|
+
<CustomerCreationModal {...defaultProps} customerData={{ name: '', email: 'a@b.co', mobile: '' }} />
|
|
101
|
+
</TestWrapper>
|
|
102
|
+
);
|
|
103
|
+
const saveBtn = screen.getByRole('button', { name: /save/i });
|
|
104
|
+
fireEvent.click(saveBtn);
|
|
105
|
+
await waitFor(() => expect(defaultProps.onSave).toHaveBeenCalled());
|
|
106
|
+
expect(defaultProps.onSave).toHaveBeenCalledWith(
|
|
107
|
+
{ email: '', mobile: '' },
|
|
108
|
+
expect.any(Function),
|
|
109
|
+
);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it('disables Save when required email is missing (EMAIL channel)', () => {
|
|
113
|
+
render(
|
|
114
|
+
<TestWrapper>
|
|
115
|
+
<CustomerCreationModal
|
|
116
|
+
{...defaultProps}
|
|
117
|
+
channel={CHANNELS.EMAIL}
|
|
118
|
+
customerData={{ name: '', email: '', mobile: '' }}
|
|
119
|
+
/>
|
|
120
|
+
</TestWrapper>
|
|
121
|
+
);
|
|
122
|
+
const saveBtn = screen.getByRole('button', { name: /save/i });
|
|
123
|
+
expect(saveBtn.disabled).toBe(true);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it('disables Save when required mobile is missing (SMS channel)', () => {
|
|
127
|
+
render(
|
|
128
|
+
<TestWrapper>
|
|
129
|
+
<CustomerCreationModal
|
|
130
|
+
{...defaultProps}
|
|
131
|
+
channel={CHANNELS.SMS}
|
|
132
|
+
customerData={{ name: '', email: '', mobile: '' }}
|
|
133
|
+
/>
|
|
134
|
+
</TestWrapper>
|
|
135
|
+
);
|
|
136
|
+
const saveBtn = screen.getByRole('button', { name: /save/i });
|
|
137
|
+
expect(saveBtn.disabled).toBe(true);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it('shows (Optional) on name label when channel is SMS', () => {
|
|
141
|
+
render(
|
|
142
|
+
<TestWrapper>
|
|
143
|
+
<CustomerCreationModal
|
|
144
|
+
{...defaultProps}
|
|
145
|
+
channel={CHANNELS.SMS}
|
|
146
|
+
customerData={{ name: '', email: '', mobile: '' }}
|
|
147
|
+
/>
|
|
148
|
+
</TestWrapper>
|
|
149
|
+
);
|
|
150
|
+
expect(screen.getAllByText(/\(optional\)/i)).toHaveLength(1);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it('shows (Optional) on name label when channel is EMAIL', () => {
|
|
154
|
+
render(
|
|
155
|
+
<TestWrapper>
|
|
156
|
+
<CustomerCreationModal
|
|
157
|
+
{...defaultProps}
|
|
158
|
+
channel={CHANNELS.EMAIL}
|
|
159
|
+
customerData={{ name: '', email: '', mobile: '' }}
|
|
160
|
+
/>
|
|
161
|
+
</TestWrapper>
|
|
162
|
+
);
|
|
163
|
+
expect(screen.getAllByText(/\(optional\)/i)).toHaveLength(1);
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
it('renders modal description', () => {
|
|
167
|
+
render(
|
|
168
|
+
<TestWrapper>
|
|
169
|
+
<CustomerCreationModal {...defaultProps} />
|
|
170
|
+
</TestWrapper>
|
|
171
|
+
);
|
|
172
|
+
expect(screen.getByText(/this customer profile will be available for testing/i)).toBeTruthy();
|
|
173
|
+
});
|
|
174
|
+
});
|
|
@@ -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
|
+
onCloseCustomerModal: 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 onCloseCustomerModal 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.onCloseCustomerModal).toHaveBeenCalled();
|
|
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
|
+
});
|
|
@@ -138,6 +138,9 @@ describe('SendTestMessage', () => {
|
|
|
138
138
|
smsTraiDltEnabled: false,
|
|
139
139
|
registeredSenderIds: [],
|
|
140
140
|
isChannelSmsFallbackPreviewEnabled: false,
|
|
141
|
+
renderAddTestCustomerButton: jest.fn(() => null),
|
|
142
|
+
searchValue: '',
|
|
143
|
+
setSearchValue: jest.fn(),
|
|
141
144
|
};
|
|
142
145
|
|
|
143
146
|
beforeEach(() => {
|
|
@@ -654,4 +657,53 @@ describe('SendTestMessage', () => {
|
|
|
654
657
|
consoleSpy.mockRestore();
|
|
655
658
|
});
|
|
656
659
|
});
|
|
660
|
+
|
|
661
|
+
describe('notFoundContent (Add as test customer)', () => {
|
|
662
|
+
it('should call renderAddTestCustomerButton and use result as notFoundContent', () => {
|
|
663
|
+
const renderAddTestCustomerButton = jest.fn(() => (
|
|
664
|
+
<button type="button" data-testid="add-test-customer-btn">
|
|
665
|
+
Add as test customer
|
|
666
|
+
</button>
|
|
667
|
+
));
|
|
668
|
+
const props = {
|
|
669
|
+
...defaultProps,
|
|
670
|
+
renderAddTestCustomerButton,
|
|
671
|
+
};
|
|
672
|
+
render(
|
|
673
|
+
<TestWrapper>
|
|
674
|
+
<SendTestMessage {...props} />
|
|
675
|
+
</TestWrapper>
|
|
676
|
+
);
|
|
677
|
+
expect(renderAddTestCustomerButton).toHaveBeenCalled();
|
|
678
|
+
});
|
|
679
|
+
|
|
680
|
+
it('should render notFoundContent as a clickable button when provided', () => {
|
|
681
|
+
const onAddClick = jest.fn();
|
|
682
|
+
const renderAddTestCustomerButton = jest.fn(() => (
|
|
683
|
+
<button type="button" data-testid="add-test-customer-btn" onClick={onAddClick}>
|
|
684
|
+
Add as test customer
|
|
685
|
+
</button>
|
|
686
|
+
));
|
|
687
|
+
const props = {
|
|
688
|
+
...defaultProps,
|
|
689
|
+
renderAddTestCustomerButton,
|
|
690
|
+
};
|
|
691
|
+
const { container } = render(
|
|
692
|
+
<TestWrapper>
|
|
693
|
+
<SendTestMessage {...props} />
|
|
694
|
+
</TestWrapper>
|
|
695
|
+
);
|
|
696
|
+
// notFoundContent is passed to TreeSelect and shown when dropdown is open with no match.
|
|
697
|
+
// We assert the render function returns a button that can be clicked.
|
|
698
|
+
const notFoundContent = renderAddTestCustomerButton.mock.results[0]?.value;
|
|
699
|
+
expect(notFoundContent).toBeTruthy();
|
|
700
|
+
expect(notFoundContent.props['data-testid']).toBe('add-test-customer-btn');
|
|
701
|
+
expect(notFoundContent.type).toBe('button');
|
|
702
|
+
// Simulate click on the returned element's onClick
|
|
703
|
+
if (notFoundContent.props.onClick) {
|
|
704
|
+
notFoundContent.props.onClick();
|
|
705
|
+
expect(onAddClick).toHaveBeenCalled();
|
|
706
|
+
}
|
|
707
|
+
});
|
|
708
|
+
});
|
|
657
709
|
});
|
|
@@ -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
|
|
@@ -206,6 +211,31 @@ describe('CommonTestAndPreview Constants', () => {
|
|
|
206
211
|
});
|
|
207
212
|
});
|
|
208
213
|
|
|
214
|
+
describe('Identifier Type Constants', () => {
|
|
215
|
+
it('should export identifier type constants', () => {
|
|
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-input-error');
|
|
236
|
+
});
|
|
237
|
+
});
|
|
238
|
+
|
|
209
239
|
describe('Channel Name Constants', () => {
|
|
210
240
|
it('should export channel name constants', () => {
|
|
211
241
|
expect(CHANNEL_NAME_INAPP).toBe('INAPP');
|
|
@@ -86,6 +86,12 @@ jest.mock('../../../utils/cdnTransformation', () => ({
|
|
|
86
86
|
getCdnUrl: jest.fn(({ url }) => `cdn_${url}`),
|
|
87
87
|
}));
|
|
88
88
|
|
|
89
|
+
// Mock services/api for add test customer flow
|
|
90
|
+
jest.mock('../../../services/api', () => ({
|
|
91
|
+
getMembersLookup: jest.fn(),
|
|
92
|
+
createTestCustomer: jest.fn(),
|
|
93
|
+
}));
|
|
94
|
+
|
|
89
95
|
// Mock messages - using actual message IDs from messages.js
|
|
90
96
|
const mockMessages = {
|
|
91
97
|
'app.v2Components.TestAndPreviewSlidebox.testAndPreviewHeader': { defaultMessage: 'Preview and Test' },
|
|
@@ -131,6 +137,7 @@ describe('CommonTestAndPreview', () => {
|
|
|
131
137
|
clearPreviewErrors: jest.fn(),
|
|
132
138
|
getSenderDetailsRequested: jest.fn(),
|
|
133
139
|
getWeCrmAccountsRequested: jest.fn(),
|
|
140
|
+
addTestCustomer: jest.fn(),
|
|
134
141
|
};
|
|
135
142
|
|
|
136
143
|
const defaultProps = {
|
|
@@ -1603,6 +1610,35 @@ describe('CommonTestAndPreview', () => {
|
|
|
1603
1610
|
|
|
1604
1611
|
expect(screen.getByTestId('send-test-message')).toBeTruthy();
|
|
1605
1612
|
});
|
|
1613
|
+
|
|
1614
|
+
it('should show error notification when sendTestMessageRequested callback receives false', async () => {
|
|
1615
|
+
const CapNotification = require('@capillarytech/cap-ui-library/CapNotification');
|
|
1616
|
+
mockActions.createMessageMetaRequested.mockImplementation((payload, metaId, cb) => {
|
|
1617
|
+
if (cb) cb({ entity: 'meta-123' });
|
|
1618
|
+
});
|
|
1619
|
+
mockActions.sendTestMessageRequested.mockImplementation((payload, cb) => {
|
|
1620
|
+
if (cb) cb(false);
|
|
1621
|
+
});
|
|
1622
|
+
const props = {
|
|
1623
|
+
...defaultProps,
|
|
1624
|
+
selectedTestEntities: ['user-1'],
|
|
1625
|
+
testGroups: [],
|
|
1626
|
+
};
|
|
1627
|
+
|
|
1628
|
+
render(
|
|
1629
|
+
<TestWrapper>
|
|
1630
|
+
<CommonTestAndPreview {...props} />
|
|
1631
|
+
</TestWrapper>
|
|
1632
|
+
);
|
|
1633
|
+
|
|
1634
|
+
expect(lastSendTestMessageProps).toBeTruthy();
|
|
1635
|
+
expect(lastSendTestMessageProps.handleSendTestMessage).toBeDefined();
|
|
1636
|
+
lastSendTestMessageProps.handleSendTestMessage();
|
|
1637
|
+
|
|
1638
|
+
await waitFor(() => {
|
|
1639
|
+
expect(CapNotification.error).toHaveBeenCalled();
|
|
1640
|
+
});
|
|
1641
|
+
});
|
|
1606
1642
|
});
|
|
1607
1643
|
|
|
1608
1644
|
describe('Content Extraction', () => {
|
|
@@ -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', () => {
|
|
@@ -2990,7 +2990,6 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
2990
2990
|
selectedOfferDetails={this.props.selectedOfferDetails}
|
|
2991
2991
|
eventContextTags={this.props?.eventContextTags}
|
|
2992
2992
|
restrictPersonalization={this.props.restrictPersonalization}
|
|
2993
|
-
waitEventContextTags={this.props?.waitEventContextTags}
|
|
2994
2993
|
/>
|
|
2995
2994
|
</CapColumn>
|
|
2996
2995
|
);
|
|
@@ -3020,7 +3019,6 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
3020
3019
|
userLocale={this.props.userLocale}
|
|
3021
3020
|
selectedOfferDetails={this.props.selectedOfferDetails}
|
|
3022
3021
|
eventContextTags={this.props?.eventContextTags}
|
|
3023
|
-
waitEventContextTags={this.props?.waitEventContextTags}
|
|
3024
3022
|
moduleFilterEnabled={this.props.location && this.props.location.query && this.props.location.query.type !== 'embedded'}
|
|
3025
3023
|
containerStyle={val.style || {}}
|
|
3026
3024
|
inputProps={val.inputProps || {}}
|
|
@@ -3660,7 +3658,6 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
3660
3658
|
channel={channel}
|
|
3661
3659
|
eventContextTags={this.props?.eventContextTags}
|
|
3662
3660
|
restrictPersonalization={this.props.restrictPersonalization}
|
|
3663
|
-
waitEventContextTags={this.props?.waitEventContextTags}
|
|
3664
3661
|
getPopupContainer={this.props.tagListGetPopupContainer}
|
|
3665
3662
|
popoverOverlayStyle={this.props.tagListPopoverOverlayStyle}
|
|
3666
3663
|
popoverOverlayClassName={this.props.tagListPopoverOverlayClassName}
|
|
@@ -3710,7 +3707,6 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
3710
3707
|
userLocale={this.state.translationLang}
|
|
3711
3708
|
selectedOfferDetails={this.props.selectedOfferDetails}
|
|
3712
3709
|
eventContextTags={this.props?.eventContextTags}
|
|
3713
|
-
waitEventContextTags={this.props?.waitEventContextTags}
|
|
3714
3710
|
moduleFilterEnabled={moduleFilterEnabledForCapTagList}
|
|
3715
3711
|
containerStyle={val.style || {}}
|
|
3716
3712
|
inputProps={val.inputProps || {}}
|
|
@@ -4005,7 +4001,6 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
4005
4001
|
onContextChange={this.props.onContextChange}
|
|
4006
4002
|
moduleFilterEnabled={isModuleFilterEnabled}
|
|
4007
4003
|
eventContextTags={this.props?.eventContextTags}
|
|
4008
|
-
waitEventContextTags={this.props?.waitEventContextTags}
|
|
4009
4004
|
/>
|
|
4010
4005
|
</CapColumn>
|
|
4011
4006
|
);
|
|
@@ -4308,7 +4303,6 @@ FormBuilder.defaultProps = {
|
|
|
4308
4303
|
userLocale: localStorage.getItem('jlocale') || 'en',
|
|
4309
4304
|
showLiquidErrorInFooter: () => {},
|
|
4310
4305
|
metaDataStatus: "",
|
|
4311
|
-
waitEventContextTags: {},
|
|
4312
4306
|
isTestAndPreviewMode: false, // Default to false to maintain existing behavior
|
|
4313
4307
|
};
|
|
4314
4308
|
|
|
@@ -4359,7 +4353,6 @@ FormBuilder.propTypes = {
|
|
|
4359
4353
|
moduleType: PropTypes.string.isRequired,
|
|
4360
4354
|
showLiquidErrorInFooter: PropTypes.func.isRequired,
|
|
4361
4355
|
eventContextTags: PropTypes.array.isRequired,
|
|
4362
|
-
waitEventContextTags: PropTypes.object,
|
|
4363
4356
|
forwardedTags: PropTypes.object.isRequired,
|
|
4364
4357
|
isLoyaltyModule: PropTypes.bool.isRequired,
|
|
4365
4358
|
isTestAndPreviewMode: PropTypes.bool, // Add new prop type
|
|
@@ -94,7 +94,6 @@ const HTMLEditor = forwardRef(({
|
|
|
94
94
|
injectedTags = {},
|
|
95
95
|
location,
|
|
96
96
|
eventContextTags = [],
|
|
97
|
-
waitEventContextTags,
|
|
98
97
|
selectedOfferDetails = [],
|
|
99
98
|
channel,
|
|
100
99
|
userLocale = 'en',
|
|
@@ -362,7 +361,7 @@ const HTMLEditor = forwardRef(({
|
|
|
362
361
|
const issueCounts = calculateIssueCounts();
|
|
363
362
|
const isContentEmpty = !currentContent || currentContent.trim() === '';
|
|
364
363
|
|
|
365
|
-
// hasErrors = only Rule Group #1 (Input & Sanitization)
|
|
364
|
+
// hasErrors = only Rule Group #1 (Input & Sanitization) – gates Done/Update/Preview/Test
|
|
366
365
|
const newState = {
|
|
367
366
|
isContentEmpty,
|
|
368
367
|
issueCounts,
|
|
@@ -664,7 +663,6 @@ const HTMLEditor = forwardRef(({
|
|
|
664
663
|
injectedTags={injectedTags}
|
|
665
664
|
location={location}
|
|
666
665
|
eventContextTags={eventContextTags}
|
|
667
|
-
waitEventContextTags={waitEventContextTags}
|
|
668
666
|
selectedOfferDetails={selectedOfferDetails}
|
|
669
667
|
channel={channel}
|
|
670
668
|
userLocale={userLocale}
|
|
@@ -736,7 +734,6 @@ const HTMLEditor = forwardRef(({
|
|
|
736
734
|
injectedTags={injectedTags}
|
|
737
735
|
location={location}
|
|
738
736
|
eventContextTags={eventContextTags}
|
|
739
|
-
waitEventContextTags={waitEventContextTags}
|
|
740
737
|
selectedOfferDetails={selectedOfferDetails}
|
|
741
738
|
channel={channel}
|
|
742
739
|
userLocale={userLocale}
|
|
@@ -775,7 +772,6 @@ HTMLEditor.propTypes = {
|
|
|
775
772
|
injectedTags: PropTypes.object,
|
|
776
773
|
location: PropTypes.object,
|
|
777
774
|
eventContextTags: PropTypes.array,
|
|
778
|
-
waitEventContextTags: PropTypes.object,
|
|
779
775
|
selectedOfferDetails: PropTypes.array,
|
|
780
776
|
channel: PropTypes.string,
|
|
781
777
|
userLocale: PropTypes.string,
|
|
@@ -809,7 +805,6 @@ HTMLEditor.defaultProps = {
|
|
|
809
805
|
injectedTags: {},
|
|
810
806
|
location: null,
|
|
811
807
|
eventContextTags: [],
|
|
812
|
-
waitEventContextTags: {},
|
|
813
808
|
selectedOfferDetails: [],
|
|
814
809
|
channel: null,
|
|
815
810
|
userLocale: 'en',
|