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.
- package/CHANGELOG.md +37 -0
- package/Jenkinsfile +2 -5
- package/api/getGraphData/index.js +3 -4
- package/package.json +2 -2
- package/tabs/package-lock.json +2927 -1866
- package/tabs/package.json +3 -3
- package/tabs/public/auth-start.html +2 -2
- package/tabs/src/components/App.test.jsx +67 -0
- package/tabs/src/components/BottomMenu.test.jsx +94 -0
- package/tabs/src/components/CustomColumnResizeIcon.jsx +1 -1
- package/tabs/src/components/CustomColumnResizeIcon.test.jsx +17 -0
- package/tabs/src/components/CustomDrawer.test.jsx +14 -0
- package/tabs/src/components/EventDialogTitle.test.jsx +25 -0
- package/tabs/src/components/HtmlBox.test.jsx +41 -0
- package/tabs/src/components/Privacy.test.jsx +11 -0
- package/tabs/src/components/ResizableGrid.test.jsx +64 -0
- package/tabs/src/components/Tab.test.jsx +463 -0
- package/tabs/src/components/TabConfig.test.jsx +27 -0
- package/tabs/src/components/TabPanel.test.jsx +31 -0
- package/tabs/src/components/TermsOfUse.test.jsx +11 -0
- package/tabs/src/components/UnderConstruction.test.jsx +13 -0
- package/tabs/src/components/UserMenu.test.jsx +53 -0
- package/tabs/src/components/activity/Activity.test.jsx +218 -0
- package/tabs/src/components/activity/ConsultationList.test.jsx +114 -0
- package/tabs/src/components/activity/EventList.test.jsx +164 -0
- package/tabs/src/components/activity/GroupsTags.test.jsx +23 -0
- package/tabs/src/components/activity/ObligationList.test.jsx +46 -0
- package/tabs/src/components/activity/PublicationList.test.jsx +46 -0
- package/tabs/src/components/activity/Reporting.test.jsx +11 -0
- package/tabs/src/components/event_rating/EventRating.test.jsx +63 -0
- package/tabs/src/components/event_rating/EventRatingDialog.test.jsx +28 -0
- package/tabs/src/components/event_registration/Approval.test.jsx +25 -0
- package/tabs/src/components/event_registration/ApprovalDialog.test.jsx +25 -0
- package/tabs/src/components/event_registration/ApprovalList.test.jsx +36 -0
- package/tabs/src/components/event_registration/EventExternalRegistration.test.jsx +219 -0
- package/tabs/src/components/event_registration/EventRegistration.test.jsx +208 -0
- package/tabs/src/components/my_country/AtAGlance.test.jsx +157 -0
- package/tabs/src/components/my_country/CountryMembers.test.jsx +117 -0
- package/tabs/src/components/my_country/CountryProgress.test.jsx +21 -0
- package/tabs/src/components/my_country/DataReporters.jsx +2 -3
- package/tabs/src/components/my_country/DataReporters.test.jsx +155 -0
- package/tabs/src/components/my_country/FullCircularProgress.test.jsx +19 -0
- package/tabs/src/components/my_country/GroupView.test.jsx +165 -0
- package/tabs/src/components/my_country/GroupsBoard.test.jsx +30 -0
- package/tabs/src/components/my_country/IndicatorCard.test.jsx +27 -0
- package/tabs/src/components/my_country/ManagementBoard.test.jsx +119 -0
- package/tabs/src/components/my_country/MyCountry.test.jsx +220 -0
- package/tabs/src/components/my_country/ProgressGauge.test.jsx +34 -0
- package/tabs/src/components/my_country/ScientificCommittee.test.jsx +11 -0
- package/tabs/src/components/my_country/UserCard.test.jsx +24 -0
- package/tabs/src/components/my_country/YearlyProgress.test.jsx +33 -0
- package/tabs/src/components/self_service/UserEdit.test.jsx +213 -0
- package/tabs/src/data/apiProvider.test.js +228 -0
- package/tabs/src/data/icsHelper.test.js +76 -0
- package/tabs/src/data/provider.test.js +351 -0
- package/tabs/src/data/reportingProvider.test.js +103 -0
- package/tabs/src/data/selfServiceProvider.test.js +108 -0
- package/tabs/src/data/selfServiceSharepointProvider.test.js +100 -0
- package/tabs/src/data/sharepointProvider.test.js +669 -0
- package/tabs/src/data/validator.test.js +34 -2
- package/tabs/yarn.lock +415 -414
- 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
|
+
});
|