@capillarytech/creatives-library 8.0.330 → 8.0.331-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/package.json +1 -1
  2. package/services/api.js +17 -0
  3. package/services/tests/api.test.js +72 -0
  4. package/utils/commonUtils.js +10 -0
  5. package/utils/tests/commonUtil.test.js +169 -0
  6. package/v2Components/CommonTestAndPreview/AddTestCustomer.js +42 -0
  7. package/v2Components/CommonTestAndPreview/CustomerCreationModal.js +155 -0
  8. package/v2Components/CommonTestAndPreview/ExistingCustomerModal.js +94 -0
  9. package/v2Components/CommonTestAndPreview/SendTestMessage.js +78 -49
  10. package/v2Components/CommonTestAndPreview/_commonTestAndPreview.scss +134 -34
  11. package/v2Components/CommonTestAndPreview/actions.js +10 -0
  12. package/v2Components/CommonTestAndPreview/constants.js +17 -1
  13. package/v2Components/CommonTestAndPreview/index.js +356 -22
  14. package/v2Components/CommonTestAndPreview/messages.js +106 -0
  15. package/v2Components/CommonTestAndPreview/reducer.js +12 -0
  16. package/v2Components/CommonTestAndPreview/sagas.js +2 -1
  17. package/v2Components/CommonTestAndPreview/tests/AddTestCustomer.test.js +66 -0
  18. package/v2Components/CommonTestAndPreview/tests/CommonTestAndPreview.addTestCustomer.test.js +648 -0
  19. package/v2Components/CommonTestAndPreview/tests/CustomValuesEditor.test.js +23 -5
  20. package/v2Components/CommonTestAndPreview/tests/CustomerCreationModal.test.js +174 -0
  21. package/v2Components/CommonTestAndPreview/tests/ExistingCustomerModal.test.js +114 -0
  22. package/v2Components/CommonTestAndPreview/tests/SendTestMessage.test.js +39 -19
  23. package/v2Components/CommonTestAndPreview/tests/constants.test.js +31 -1
  24. package/v2Components/CommonTestAndPreview/tests/index.test.js +36 -0
  25. package/v2Components/CommonTestAndPreview/tests/reducer.test.js +71 -0
  26. package/v2Components/CommonTestAndPreview/tests/selectors.test.js +17 -0
  27. package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +1408 -1276
  28. package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +321 -288
  29. package/v2Containers/TagList/index.js +11 -15
  30. package/v2Containers/WebPush/Create/index.js +1 -1
  31. package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +5246 -4872
@@ -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
+ });
@@ -135,6 +135,10 @@ describe('SendTestMessage', () => {
135
135
  isLoadingSenderDetails: false,
136
136
  smsTraiDltEnabled: false,
137
137
  registeredSenderIds: [],
138
+ isChannelSmsFallbackPreviewEnabled: false,
139
+ renderAddTestCustomerButton: jest.fn(() => null),
140
+ searchValue: '',
141
+ setSearchValue: jest.fn(),
138
142
  };
139
143
 
140
144
  beforeEach(() => {
@@ -634,35 +638,51 @@ describe('SendTestMessage', () => {
634
638
  });
635
639
  });
636
640
 
637
- describe('whatsappAccountFromForm with null/undefined formData', () => {
638
- it('passes whatsappAccountFromForm as undefined when channel is WHATSAPP and formData is null', () => {
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
+ };
639
652
  render(
640
653
  <TestWrapper>
641
- <SendTestMessage
642
- {...defaultProps}
643
- channel="WHATSAPP"
644
- formData={null}
645
- />
654
+ <SendTestMessage {...props} />
646
655
  </TestWrapper>
647
656
  );
648
- const el = screen.getByTestId('delivery-settings');
649
- const data = JSON.parse(el.getAttribute('data-props'));
650
- expect(data.whatsappAccountFromForm).toBeUndefined();
657
+ expect(renderAddTestCustomerButton).toHaveBeenCalled();
651
658
  });
652
659
 
653
- it('passes whatsappAccountFromForm as undefined when channel is WHATSAPP and formData is undefined', () => {
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
+ };
654
671
  render(
655
672
  <TestWrapper>
656
- <SendTestMessage
657
- {...defaultProps}
658
- channel="WHATSAPP"
659
- formData={undefined}
660
- />
673
+ <SendTestMessage {...props} />
661
674
  </TestWrapper>
662
675
  );
663
- const el = screen.getByTestId('delivery-settings');
664
- const data = JSON.parse(el.getAttribute('data-props'));
665
- expect(data.whatsappAccountFromForm).toBeUndefined();
676
+ // Assert component wiring: renderAddTestCustomerButton must have been called during render.
677
+ expect(renderAddTestCustomerButton).toHaveBeenCalled();
678
+ // Assert the returned element has the expected shape.
679
+ const notFoundContent = renderAddTestCustomerButton.mock.results[0].value;
680
+ expect(notFoundContent).toBeTruthy();
681
+ expect(notFoundContent.type).toBe('button');
682
+ expect(notFoundContent.props['data-testid']).toBe('add-test-customer-btn');
683
+ // Assert clicking the button triggers the handler.
684
+ notFoundContent.props.onClick();
685
+ expect(onAddClick).toHaveBeenCalled();
666
686
  });
667
687
  });
668
688
  });
@@ -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');
@@ -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 = {
@@ -1578,6 +1585,35 @@ describe('CommonTestAndPreview', () => {
1578
1585
 
1579
1586
  expect(screen.getByTestId('send-test-message')).toBeTruthy();
1580
1587
  });
1588
+
1589
+ it('should show error notification when sendTestMessageRequested callback receives false', async () => {
1590
+ const CapNotification = require('@capillarytech/cap-ui-library/CapNotification');
1591
+ mockActions.createMessageMetaRequested.mockImplementation((payload, metaId, cb) => {
1592
+ if (cb) cb({ entity: 'meta-123' });
1593
+ });
1594
+ mockActions.sendTestMessageRequested.mockImplementation((payload, cb) => {
1595
+ if (cb) cb(false);
1596
+ });
1597
+ const props = {
1598
+ ...defaultProps,
1599
+ selectedTestEntities: ['user-1'],
1600
+ testGroups: [],
1601
+ };
1602
+
1603
+ render(
1604
+ <TestWrapper>
1605
+ <CommonTestAndPreview {...props} />
1606
+ </TestWrapper>
1607
+ );
1608
+
1609
+ expect(lastSendTestMessageProps).toBeTruthy();
1610
+ expect(lastSendTestMessageProps.handleSendTestMessage).toBeDefined();
1611
+ lastSendTestMessageProps.handleSendTestMessage();
1612
+
1613
+ await waitFor(() => {
1614
+ expect(CapNotification.error).toHaveBeenCalled();
1615
+ });
1616
+ });
1581
1617
  });
1582
1618
 
1583
1619
  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', () => {