eionet2-dashboard 3.2.1 → 3.2.3

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 (62) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/Jenkinsfile +2 -5
  3. package/api/getGraphData/index.js +3 -4
  4. package/package.json +2 -2
  5. package/tabs/package-lock.json +2927 -1866
  6. package/tabs/package.json +3 -3
  7. package/tabs/public/auth-start.html +2 -2
  8. package/tabs/src/components/App.test.jsx +67 -0
  9. package/tabs/src/components/BottomMenu.test.jsx +94 -0
  10. package/tabs/src/components/CustomColumnResizeIcon.jsx +1 -1
  11. package/tabs/src/components/CustomColumnResizeIcon.test.jsx +17 -0
  12. package/tabs/src/components/CustomDrawer.test.jsx +14 -0
  13. package/tabs/src/components/EventDialogTitle.test.jsx +25 -0
  14. package/tabs/src/components/HtmlBox.test.jsx +41 -0
  15. package/tabs/src/components/Privacy.test.jsx +11 -0
  16. package/tabs/src/components/ResizableGrid.test.jsx +64 -0
  17. package/tabs/src/components/Tab.test.jsx +463 -0
  18. package/tabs/src/components/TabConfig.test.jsx +27 -0
  19. package/tabs/src/components/TabPanel.test.jsx +31 -0
  20. package/tabs/src/components/TermsOfUse.test.jsx +11 -0
  21. package/tabs/src/components/UnderConstruction.test.jsx +13 -0
  22. package/tabs/src/components/UserMenu.test.jsx +53 -0
  23. package/tabs/src/components/activity/Activity.test.jsx +218 -0
  24. package/tabs/src/components/activity/ConsultationList.test.jsx +114 -0
  25. package/tabs/src/components/activity/EventList.test.jsx +164 -0
  26. package/tabs/src/components/activity/GroupsTags.test.jsx +23 -0
  27. package/tabs/src/components/activity/ObligationList.test.jsx +46 -0
  28. package/tabs/src/components/activity/PublicationList.test.jsx +46 -0
  29. package/tabs/src/components/activity/Reporting.test.jsx +11 -0
  30. package/tabs/src/components/event_rating/EventRating.test.jsx +63 -0
  31. package/tabs/src/components/event_rating/EventRatingDialog.test.jsx +28 -0
  32. package/tabs/src/components/event_registration/Approval.test.jsx +25 -0
  33. package/tabs/src/components/event_registration/ApprovalDialog.test.jsx +25 -0
  34. package/tabs/src/components/event_registration/ApprovalList.test.jsx +36 -0
  35. package/tabs/src/components/event_registration/EventExternalRegistration.test.jsx +219 -0
  36. package/tabs/src/components/event_registration/EventRegistration.test.jsx +208 -0
  37. package/tabs/src/components/my_country/AtAGlance.test.jsx +157 -0
  38. package/tabs/src/components/my_country/CountryMembers.test.jsx +117 -0
  39. package/tabs/src/components/my_country/CountryProgress.test.jsx +21 -0
  40. package/tabs/src/components/my_country/DataReporters.jsx +2 -3
  41. package/tabs/src/components/my_country/DataReporters.test.jsx +155 -0
  42. package/tabs/src/components/my_country/FullCircularProgress.test.jsx +19 -0
  43. package/tabs/src/components/my_country/GroupView.test.jsx +165 -0
  44. package/tabs/src/components/my_country/GroupsBoard.test.jsx +30 -0
  45. package/tabs/src/components/my_country/IndicatorCard.test.jsx +27 -0
  46. package/tabs/src/components/my_country/ManagementBoard.test.jsx +119 -0
  47. package/tabs/src/components/my_country/MyCountry.test.jsx +220 -0
  48. package/tabs/src/components/my_country/ProgressGauge.test.jsx +34 -0
  49. package/tabs/src/components/my_country/ScientificCommittee.test.jsx +11 -0
  50. package/tabs/src/components/my_country/UserCard.test.jsx +24 -0
  51. package/tabs/src/components/my_country/YearlyProgress.test.jsx +33 -0
  52. package/tabs/src/components/self_service/UserEdit.test.jsx +213 -0
  53. package/tabs/src/data/apiProvider.test.js +228 -0
  54. package/tabs/src/data/icsHelper.test.js +76 -0
  55. package/tabs/src/data/provider.test.js +351 -0
  56. package/tabs/src/data/reportingProvider.test.js +103 -0
  57. package/tabs/src/data/selfServiceProvider.test.js +108 -0
  58. package/tabs/src/data/selfServiceSharepointProvider.test.js +100 -0
  59. package/tabs/src/data/sharepointProvider.test.js +669 -0
  60. package/tabs/src/data/validator.test.js +34 -2
  61. package/tabs/yarn.lock +415 -414
  62. package/tabs/src/components/CustomGridToolbar.jsx +0 -202
@@ -0,0 +1,220 @@
1
+ jest.mock('react', () => {
2
+ const actual = jest.requireActual('react');
3
+ return {
4
+ ...actual,
5
+ React: actual,
6
+ useState: jest.fn(actual.useState),
7
+ useEffect: jest.fn((fn) => fn()),
8
+ useCallback: jest.fn((fn) => fn),
9
+ };
10
+ });
11
+
12
+ import React from 'react';
13
+ import { renderToStaticMarkup } from 'react-dom/server';
14
+ import { MyCountry } from './MyCountry';
15
+ import {
16
+ getMappingsList,
17
+ getInvitedUsers,
18
+ getOrganisationList,
19
+ getAvailableGroups,
20
+ getCountryCodeMappingsList,
21
+ } from '../../data/sharepointProvider';
22
+ import { useAppInsightsContext } from '@microsoft/applicationinsights-react-js';
23
+
24
+ jest.mock('./my_country.scss', () => ({}));
25
+
26
+ jest.mock('@mui/material', () => {
27
+ const ReactLocal = require('react');
28
+ const passthrough =
29
+ (Tag = 'div') =>
30
+ ({ children }) =>
31
+ ReactLocal.createElement(Tag, {}, children);
32
+
33
+ const ListItemButton = ({ children, onClick }) => {
34
+ onClick && onClick();
35
+ return <div>{children}</div>;
36
+ };
37
+
38
+ return {
39
+ Backdrop: passthrough(),
40
+ Box: passthrough(),
41
+ CircularProgress: passthrough('span'),
42
+ Typography: passthrough('span'),
43
+ ListItem: passthrough(),
44
+ ListItemText: ({ primary }) => <div>{primary}</div>,
45
+ ListItemButton,
46
+ ListItemIcon: passthrough(),
47
+ };
48
+ });
49
+
50
+ jest.mock('@mui/icons-material/Preview', () => () => <span>preview-icon</span>);
51
+ jest.mock('@mui/icons-material/ManageAccounts', () => () => <span>manage-accounts-icon</span>);
52
+ jest.mock('@mui/icons-material/Group', () => () => <span>group-icon</span>);
53
+ jest.mock('@mui/icons-material/Groups', () => () => <span>groups-icon</span>);
54
+ jest.mock('@mui/icons-material/GroupWork', () => () => <span>group-work-icon</span>);
55
+ jest.mock('@mui/icons-material/Summarize', () => () => <span>summarize-icon</span>);
56
+
57
+ jest.mock('../../data/sharepointProvider', () => ({
58
+ getMappingsList: jest.fn(),
59
+ getInvitedUsers: jest.fn(),
60
+ getOrganisationList: jest.fn(),
61
+ getAvailableGroups: jest.fn(),
62
+ getCountryCodeMappingsList: jest.fn(),
63
+ }));
64
+
65
+ jest.mock('@microsoft/applicationinsights-react-js', () => ({
66
+ useAppInsightsContext: jest.fn(),
67
+ }));
68
+
69
+ jest.mock('./AtAGlance', () => ({
70
+ AtAGlance: () => <div>at-a-glance</div>,
71
+ }));
72
+ jest.mock('./ManagementBoard', () => ({
73
+ ManagementBoard: () => <div>management-board</div>,
74
+ }));
75
+ jest.mock('./GroupsBoard', () => ({
76
+ GroupsBoard: () => <div>groups-board</div>,
77
+ }));
78
+ jest.mock('./CountryMembers', () => ({
79
+ CountryMembers: () => <div>country-members</div>,
80
+ }));
81
+ jest.mock('./DataReporters', () => ({
82
+ DataReporters: () => <div>data-reporters</div>,
83
+ }));
84
+ jest.mock('../CustomDrawer', () => ({
85
+ __esModule: true,
86
+ default: ({ drawerOptions }) => <div>{drawerOptions}</div>,
87
+ }));
88
+
89
+ function mockStateSequence(values) {
90
+ let index = 0;
91
+ React.useState.mockImplementation((initialValue) => {
92
+ if (index < values.length) {
93
+ const current = values[index];
94
+ index += 1;
95
+ return [current, jest.fn()];
96
+ }
97
+ return [initialValue, jest.fn()];
98
+ });
99
+ }
100
+
101
+ function buildStates(tabValue = 0) {
102
+ return [
103
+ tabValue,
104
+ [{ id: 1 }],
105
+ [{ OtherMembership: false }, { OtherMembership: true }],
106
+ false,
107
+ [{ id: 'org' }],
108
+ { CountryCode: 'RO', Name: 'Romania' },
109
+ ['Air', 'Climate'],
110
+ ];
111
+ }
112
+
113
+ describe('MyCountry', () => {
114
+ const trackEvent = jest.fn();
115
+ const closeDrawer = jest.fn();
116
+
117
+ const baseProps = {
118
+ userInfo: { isNFP: true },
119
+ selectedCountry: 'RO',
120
+ configuration: {},
121
+ drawerOpen: true,
122
+ closeDrawer,
123
+ };
124
+
125
+ beforeEach(() => {
126
+ jest.clearAllMocks();
127
+
128
+ React.useEffect.mockImplementation((fn) => fn());
129
+ React.useCallback.mockImplementation((fn) => fn);
130
+ React.useState.mockImplementation((initialValue) => [initialValue, jest.fn()]);
131
+
132
+ useAppInsightsContext.mockReturnValue({ trackEvent });
133
+
134
+ getInvitedUsers.mockResolvedValue([{ id: 1 }]);
135
+ getOrganisationList.mockResolvedValue([{ id: 'org' }]);
136
+ getAvailableGroups.mockResolvedValue(['wg-test', 'Air group']);
137
+ getCountryCodeMappingsList.mockResolvedValue([{ CountryCode: 'RO' }]);
138
+ getMappingsList.mockResolvedValue([{ OtherMembership: false }, { OtherMembership: true }]);
139
+ });
140
+
141
+ test('renders drawer menu and at-a-glance tab', () => {
142
+ mockStateSequence(buildStates(0));
143
+
144
+ const html = renderToStaticMarkup(<MyCountry {...baseProps} />);
145
+
146
+ expect(html).toContain('At a glance');
147
+ expect(html).toContain('NFPs');
148
+ expect(html).toContain('Eionet groups');
149
+ expect(html).toContain('ETCs');
150
+ expect(html).toContain('Country desk officers');
151
+ expect(html).toContain('Reporting');
152
+ expect(html).toContain('at-a-glance');
153
+ expect(closeDrawer).toHaveBeenCalled();
154
+ expect(trackEvent).toHaveBeenCalled();
155
+ });
156
+
157
+ test('renders management board tab', () => {
158
+ mockStateSequence(buildStates(1));
159
+
160
+ const html = renderToStaticMarkup(<MyCountry {...baseProps} />);
161
+
162
+ expect(html).toContain('management-board');
163
+ });
164
+
165
+ test('renders groups board tabs', () => {
166
+ mockStateSequence(buildStates(2));
167
+ const htmlGroups = renderToStaticMarkup(<MyCountry {...baseProps} />);
168
+ expect(htmlGroups).toContain('groups-board');
169
+
170
+ mockStateSequence(buildStates(3));
171
+ const htmlEtc = renderToStaticMarkup(<MyCountry {...baseProps} />);
172
+ expect(htmlEtc).toContain('groups-board');
173
+ });
174
+
175
+ test('renders country members and data reporters tabs', () => {
176
+ mockStateSequence(buildStates(4));
177
+ const htmlMembers = renderToStaticMarkup(<MyCountry {...baseProps} />);
178
+ expect(htmlMembers).toContain('country-members');
179
+
180
+ mockStateSequence(buildStates(5));
181
+ const htmlReporters = renderToStaticMarkup(<MyCountry {...baseProps} />);
182
+ expect(htmlReporters).toContain('data-reporters');
183
+ });
184
+
185
+ test('hides country-specific tabs when selectedCountry is missing', () => {
186
+ mockStateSequence(buildStates(0));
187
+
188
+ const html = renderToStaticMarkup(<MyCountry {...baseProps} selectedCountry={''} />);
189
+
190
+ expect(html).not.toContain('Country desk officers');
191
+ expect(html).not.toContain('Reporting');
192
+ });
193
+
194
+ test('loads data from providers', async () => {
195
+ mockStateSequence(buildStates(0));
196
+
197
+ renderToStaticMarkup(<MyCountry {...baseProps} />);
198
+ await Promise.resolve();
199
+ await Promise.resolve();
200
+
201
+ expect(getInvitedUsers).toHaveBeenCalledWith('RO');
202
+ expect(getOrganisationList).toHaveBeenCalledWith('RO');
203
+ expect(getAvailableGroups).toHaveBeenCalled();
204
+ expect(getCountryCodeMappingsList).toHaveBeenCalled();
205
+ expect(getMappingsList).toHaveBeenCalled();
206
+ });
207
+
208
+ test('handles invited users loading errors', async () => {
209
+ const logSpy = jest.spyOn(console, 'log').mockImplementation(() => {});
210
+ getInvitedUsers.mockRejectedValue(new Error('failed users'));
211
+ mockStateSequence(buildStates(0));
212
+
213
+ renderToStaticMarkup(<MyCountry {...baseProps} />);
214
+ await Promise.resolve();
215
+ await Promise.resolve();
216
+
217
+ expect(logSpy).toHaveBeenCalledWith('failed users');
218
+ logSpy.mockRestore();
219
+ });
220
+ });
@@ -0,0 +1,34 @@
1
+ import React from 'react';
2
+ import { renderToStaticMarkup } from 'react-dom/server';
3
+ import { ProgressGauge } from './ProgressGauge';
4
+
5
+ describe('ProgressGauge', () => {
6
+ test('renders label and ratio values', () => {
7
+ const html = renderToStaticMarkup(
8
+ <ProgressGauge
9
+ totalCount={20}
10
+ responseCount={8}
11
+ label="Consultations"
12
+ infoText="Info text"
13
+ />,
14
+ );
15
+
16
+ expect(html).toContain('Consultations');
17
+ expect(html).toContain('/20');
18
+ expect(html).toContain('8');
19
+ });
20
+
21
+ test('renders details link text when url exists', () => {
22
+ const html = renderToStaticMarkup(
23
+ <ProgressGauge
24
+ totalCount={2}
25
+ responseCount={1}
26
+ label="Events"
27
+ infoText="Info"
28
+ url="https://example.org"
29
+ />,
30
+ );
31
+
32
+ expect(html).toContain('Events');
33
+ });
34
+ });
@@ -0,0 +1,11 @@
1
+ import React from 'react';
2
+ import { renderToStaticMarkup } from 'react-dom/server';
3
+ import { ScientificCommittee } from './ScientificCommittee';
4
+
5
+ describe('ScientificCommittee', () => {
6
+ test('renders under construction message', () => {
7
+ const html = renderToStaticMarkup(<ScientificCommittee />);
8
+
9
+ expect(html).toContain('Page under construction');
10
+ });
11
+ });
@@ -0,0 +1,24 @@
1
+ import React from 'react';
2
+ import { renderToStaticMarkup } from 'react-dom/server';
3
+ import { UserCard } from './UserCard';
4
+
5
+ describe('UserCard', () => {
6
+ test('renders user name, email and organisation', () => {
7
+ const html = renderToStaticMarkup(
8
+ <UserCard
9
+ showAvatar={true}
10
+ userInfo={{
11
+ UserName: 'John Doe',
12
+ PhotoSrc: 'photo',
13
+ Email: 'john@example.org',
14
+ Organisation: 'EEA',
15
+ }}
16
+ />,
17
+ );
18
+
19
+ expect(html).toContain('John Doe');
20
+ expect(html).toContain('john@example.org');
21
+ expect(html).toContain('EEA');
22
+ expect(html).toContain('img');
23
+ });
24
+ });
@@ -0,0 +1,33 @@
1
+ import React from 'react';
2
+ import { renderToStaticMarkup } from 'react-dom/server';
3
+ import { YearlyProgress } from './YearlyProgress';
4
+
5
+ describe('YearlyProgress', () => {
6
+ test('renders participation header and three gauge labels', () => {
7
+ const html = renderToStaticMarkup(
8
+ <YearlyProgress
9
+ yearData={{
10
+ consultationsCount: 10,
11
+ responseConsultationsCount: 5,
12
+ consultationsUrl: 'https://example.org/c',
13
+ surveysCount: 8,
14
+ responseSurveysCount: 4,
15
+ surveysUrl: 'https://example.org/s',
16
+ meetingsCount: 6,
17
+ attendedMeetingsCount: 3,
18
+ meetingsUrl: 'https://example.org/m',
19
+ }}
20
+ configuration={{
21
+ YearlyConsultationsCountInfo: 'c',
22
+ YearlySurveysCountInfo: 's',
23
+ YearlyEventsCountInfo: 'e',
24
+ }}
25
+ />,
26
+ );
27
+
28
+ expect(html).toContain('Participation:');
29
+ expect(html).toContain('Consultations');
30
+ expect(html).toContain('Enquiries');
31
+ expect(html).toContain('Events');
32
+ });
33
+ });
@@ -0,0 +1,213 @@
1
+ jest.mock('react', () => {
2
+ const actual = jest.requireActual('react');
3
+ return {
4
+ ...actual,
5
+ React: actual,
6
+ useEffect: jest.fn((fn) => fn()),
7
+ };
8
+ });
9
+
10
+ import React from 'react';
11
+ import { renderToStaticMarkup } from 'react-dom/server';
12
+ import { UserEdit } from './UserEdit';
13
+ import { saveData } from '../../data/selfServiceProvider';
14
+ import { getGenderList } from '../../data/selfServiceSharepointProvider';
15
+ import { validateMandatoryField, validateName, validatePhone } from '../../data/validator';
16
+
17
+ const formSubmitHandlers = [];
18
+ const textFieldProps = [];
19
+ const autocompleteProps = [];
20
+
21
+ jest.mock('@mui/material', () => {
22
+ const ReactLocal = require('react');
23
+ const passthrough =
24
+ (Tag = 'div') =>
25
+ ({ children }) =>
26
+ ReactLocal.createElement(Tag, {}, children);
27
+
28
+ const Box = ({ children, component, onSubmit }) => {
29
+ if (component === 'form' && onSubmit) {
30
+ formSubmitHandlers.push(onSubmit);
31
+ }
32
+ return <div>{children}</div>;
33
+ };
34
+
35
+ const TextField = (props) => {
36
+ textFieldProps.push(props);
37
+ return <div>{props.label || props.id || ''}</div>;
38
+ };
39
+
40
+ const Autocomplete = (props) => {
41
+ autocompleteProps.push(props);
42
+ return <div>{props.renderInput ? props.renderInput({ id: 'gender' }) : null}</div>;
43
+ };
44
+
45
+ return {
46
+ Box,
47
+ TextField,
48
+ Autocomplete,
49
+ Button: passthrough('button'),
50
+ FormLabel: passthrough('label'),
51
+ CircularProgress: passthrough('span'),
52
+ Chip: ({ label }) => <div>{label}</div>,
53
+ Paper: passthrough(),
54
+ InputLabel: passthrough('label'),
55
+ Link: ({ children, href }) => <a href={href}>{children}</a>,
56
+ Typography: passthrough('span'),
57
+ Divider: passthrough('hr'),
58
+ Tooltip: ({ children }) => <div>{children}</div>,
59
+ };
60
+ });
61
+
62
+ jest.mock('@mui/icons-material/Check', () => () => <span>check-icon</span>);
63
+ jest.mock('@mui/icons-material/Save', () => () => <span>save-icon</span>);
64
+ jest.mock('@mui/icons-material/Contacts', () => () => <span>contacts-icon</span>);
65
+
66
+ jest.mock('./UserEdit.scss', () => ({}));
67
+ jest.mock('../../data/selfServiceProvider', () => ({
68
+ saveData: jest.fn(),
69
+ }));
70
+ jest.mock('../../data/selfServiceSharepointProvider', () => ({
71
+ getGenderList: jest.fn(),
72
+ }));
73
+ jest.mock('../../data/validator', () => ({
74
+ validateMandatoryField: jest.fn(),
75
+ validateName: jest.fn(),
76
+ validatePhone: jest.fn(),
77
+ }));
78
+
79
+ describe('UserEdit', () => {
80
+ const baseUser = {
81
+ Gender: 'Mr',
82
+ FirstName: 'John',
83
+ LastName: 'Doe',
84
+ JobTitle: 'Expert',
85
+ Phone: '+40123',
86
+ Department: 'Air',
87
+ Country: 'Romania',
88
+ Email: 'john.doe@example.org',
89
+ Organisation: 'EEA',
90
+ SelfSeviceHelpdeskPersonalDetailsText: 'Personal help text',
91
+ SelfSeviceHelpdeskPreferencesText: 'Preferences help text',
92
+ PCP: [],
93
+ Memberships: [],
94
+ OtherMemberships: [],
95
+ NFP: false,
96
+ };
97
+
98
+ beforeEach(() => {
99
+ jest.clearAllMocks();
100
+ formSubmitHandlers.length = 0;
101
+ textFieldProps.length = 0;
102
+ autocompleteProps.length = 0;
103
+
104
+ getGenderList.mockResolvedValue(['Mr', 'Mrs']);
105
+ validateMandatoryField.mockReturnValue('');
106
+ validateName.mockReturnValue('');
107
+ validatePhone.mockReturnValue('');
108
+ saveData.mockResolvedValue({ Success: true });
109
+ });
110
+
111
+ test('renders form content and optional blocks', () => {
112
+ const html = renderToStaticMarkup(
113
+ <UserEdit
114
+ user={{
115
+ ...baseUser,
116
+ PCP: ['Air group'],
117
+ Memberships: ['Air group', 'Climate group'],
118
+ OtherMemberships: ['External network'],
119
+ NFP: 'Romania NFP',
120
+ }}
121
+ configuration={{ DashboardLeadIconTooltip: 'Lead contact' }}
122
+ />,
123
+ );
124
+
125
+ expect(html).toContain('Manage personal details');
126
+ expect(html).toContain('Memberships');
127
+ expect(html).toContain('Other memberships');
128
+ expect(html).toContain('Romania NFP');
129
+ expect(html).toContain('helpdesk@eea.europa.eu');
130
+ expect(html).toContain('Manage preferences');
131
+ expect(getGenderList).toHaveBeenCalled();
132
+ });
133
+
134
+ test('runs submit success flow', async () => {
135
+ const user = { ...baseUser };
136
+ renderToStaticMarkup(<UserEdit user={user} configuration={{}} />);
137
+
138
+ const submit = formSubmitHandlers[0];
139
+ expect(submit).toBeDefined();
140
+
141
+ const preventDefault = jest.fn();
142
+ await submit({ preventDefault });
143
+
144
+ expect(preventDefault).toHaveBeenCalled();
145
+ expect(saveData).toHaveBeenCalledWith(user);
146
+ expect(validateMandatoryField).toHaveBeenCalledWith(user.Gender);
147
+ expect(validateName).toHaveBeenCalledWith(user.FirstName);
148
+ expect(validatePhone).toHaveBeenCalledWith(user.Phone);
149
+ });
150
+
151
+ test('runs submit error flow when save fails', async () => {
152
+ saveData.mockResolvedValue({ Success: false, Message: 'Save failed', Error: 'Backend error' });
153
+
154
+ renderToStaticMarkup(<UserEdit user={{ ...baseUser }} configuration={{}} />);
155
+
156
+ await formSubmitHandlers[0]({ preventDefault: jest.fn() });
157
+
158
+ expect(saveData).toHaveBeenCalled();
159
+ });
160
+
161
+ test('stops submit when form has validation errors', async () => {
162
+ validateMandatoryField.mockReturnValue('Required');
163
+
164
+ renderToStaticMarkup(<UserEdit user={{ ...baseUser }} configuration={{}} />);
165
+
166
+ await formSubmitHandlers[0]({ preventDefault: jest.fn() });
167
+
168
+ expect(saveData).not.toHaveBeenCalled();
169
+ });
170
+
171
+ test('triggers field handlers for validation and mutations', () => {
172
+ const user = { ...baseUser };
173
+ const logSpy = jest.spyOn(console, 'log').mockImplementation(() => {});
174
+
175
+ renderToStaticMarkup(<UserEdit user={user} configuration={{}} />);
176
+
177
+ const firstName = textFieldProps.find((p) => p.id === 'firstName');
178
+ const lastName = textFieldProps.find((p) => p.id === 'lastName' && p.label === 'Last name');
179
+ const jobTitle = textFieldProps.find((p) => p.id === 'jobTitle');
180
+ const phone = textFieldProps.find((p) => p.id === 'phone');
181
+ const department = textFieldProps.find((p) => p.id === 'department');
182
+ const salutation = textFieldProps.find((p) => p.label === 'Salutation');
183
+
184
+ firstName.onChange({ target: { id: 'firstName', value: 'Alice' } });
185
+ lastName.onChange({ target: { id: 'lastName', value: 'Smith' } });
186
+ phone.onChange({ target: { id: 'phone', value: '+49123' } });
187
+ jobTitle.onChange({ target: { id: 'jobTitle', value: 'Manager' } });
188
+ department.onChange({ target: { id: 'department', value: 'Climate' } });
189
+
190
+ salutation.onBlur({ target: { id: 'gender' } });
191
+
192
+ const autocomplete = autocompleteProps[0];
193
+ autocomplete.onChange({}, 'Mrs');
194
+
195
+ expect(user.FirstName).toBe('Alice');
196
+ expect(user.LastName).toBe('Smith');
197
+ expect(user.Phone).toBe('+49123');
198
+ expect(user.JobTitle).toBe('Manager');
199
+ expect(user.Department).toBe('Climate');
200
+ expect(user.Gender).toBe('Mrs');
201
+
202
+ expect(validateName).toHaveBeenCalled();
203
+ expect(validatePhone).toHaveBeenCalled();
204
+ expect(logSpy).toHaveBeenCalledWith('Undefined field for validation');
205
+
206
+ logSpy.mockRestore();
207
+ });
208
+
209
+ test('renders empty container when user is missing', () => {
210
+ const html = renderToStaticMarkup(<UserEdit user={null} configuration={{}} />);
211
+ expect(html).toBe('<div class=""></div>');
212
+ });
213
+ });