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,669 @@
|
|
|
1
|
+
jest.mock('./apiProvider', () => ({
|
|
2
|
+
apiGet: jest.fn(),
|
|
3
|
+
apiPost: jest.fn(),
|
|
4
|
+
apiPatch: jest.fn(),
|
|
5
|
+
getConfiguration: jest.fn(),
|
|
6
|
+
apiDelete: jest.fn(),
|
|
7
|
+
logError: jest.fn(),
|
|
8
|
+
logInfo: jest.fn(),
|
|
9
|
+
}));
|
|
10
|
+
|
|
11
|
+
jest.mock('./provider', () => ({
|
|
12
|
+
sendEmail: jest.fn(),
|
|
13
|
+
}));
|
|
14
|
+
|
|
15
|
+
jest.mock('./icsHelper', () => ({
|
|
16
|
+
createIcs: jest.fn(),
|
|
17
|
+
}));
|
|
18
|
+
|
|
19
|
+
const Constants = require('./constants.json');
|
|
20
|
+
const { sendEmail } = require('./provider');
|
|
21
|
+
const { createIcs } = require('./icsHelper');
|
|
22
|
+
|
|
23
|
+
function loadModule() {
|
|
24
|
+
let apiProvider;
|
|
25
|
+
let sharepointProvider;
|
|
26
|
+
jest.isolateModules(() => {
|
|
27
|
+
apiProvider = require('./apiProvider');
|
|
28
|
+
sharepointProvider = require('./sharepointProvider');
|
|
29
|
+
});
|
|
30
|
+
return { apiProvider, sharepointProvider };
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const baseConfig = {
|
|
34
|
+
SharepointSiteId: 'site-id',
|
|
35
|
+
OrganisationListId: 'org-list-id',
|
|
36
|
+
MappingListId: 'mapping-list-id',
|
|
37
|
+
UserListId: 'user-list-id',
|
|
38
|
+
ConsultationListId: 'consult-list-id',
|
|
39
|
+
ConsultationListItemUrl: 'https://example.org/consult-items',
|
|
40
|
+
MeetingParticipantsListId: 'participants-list-id',
|
|
41
|
+
CommunicationSiteId: 'comm-site-id',
|
|
42
|
+
PublicationListId: 'pub-list-id',
|
|
43
|
+
ObligationsListId: 'obl-list-id',
|
|
44
|
+
EventListItemUrl: 'https://example.org/event-items',
|
|
45
|
+
MeetingListId: 'meeting-list-id',
|
|
46
|
+
MeetingParticipantsListUrl: 'https://example.org/participants',
|
|
47
|
+
NoOfDaysForRating: 5,
|
|
48
|
+
CountryCodeMappingListId: 'country-map-list-id',
|
|
49
|
+
MeetingRatingListId: 'meeting-rating-list-id',
|
|
50
|
+
FromEmailAddress: 'from@example.org',
|
|
51
|
+
RegOnlineEmailSubjectUser: 'User online {MeetingTitle}',
|
|
52
|
+
RegOnlineEmailBodyUser: 'Join {MeetingTitle} at {MeetingJoinUrl}',
|
|
53
|
+
RegOfflineEmailSubjectUser: 'User offline {MeetingTitle}',
|
|
54
|
+
RegOfflineEmailBodyUser: 'Offline body {MeetingTitle}',
|
|
55
|
+
RegOnlineEmailSubjectNFP: 'NFP online {MeetingTitle}',
|
|
56
|
+
RegOnlineEmailBodyNFP: 'NFP body {MeetingTitle}',
|
|
57
|
+
RegOfflineEmailSubjectNFP: 'NFP offline {MeetingTitle}',
|
|
58
|
+
RegOfflineEmailBodyNFP: 'NFP offline body {MeetingTitle}',
|
|
59
|
+
InviteOnlineEmailSubject: 'Invite online {MeetingTitle}',
|
|
60
|
+
InviteOnlineEmailBody: 'Invite body {MeetingTitle} {MeetingJoinUrl}',
|
|
61
|
+
InviteOfflineEmailSubject: 'Invite offline {MeetingTitle}',
|
|
62
|
+
InviteOfflineEmailBody: 'Invite offline body {MeetingTitle}',
|
|
63
|
+
RegOnlineNFPAccepts: 'Accepted {MeetingTitle} {MeetingJoinUrl}',
|
|
64
|
+
RegOnlineNFPDeclines: 'Declined {MeetingTitle}',
|
|
65
|
+
RegOfflineNFPAccepts: 'Offline accepted {MeetingTitle}',
|
|
66
|
+
RegOfflineNFPDeclines: 'Offline declined {MeetingTitle}',
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
describe('sharepointProvider', () => {
|
|
70
|
+
beforeEach(() => {
|
|
71
|
+
jest.clearAllMocks();
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
test('getOrganisationList maps values', async () => {
|
|
75
|
+
const { apiProvider, sharepointProvider } = loadModule();
|
|
76
|
+
apiProvider.getConfiguration.mockResolvedValue(baseConfig);
|
|
77
|
+
apiProvider.apiGet.mockResolvedValue({
|
|
78
|
+
graphClientMessage: {
|
|
79
|
+
value: [{ id: 10, fields: { Title: 'Org 1' } }],
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
const result = await sharepointProvider.getOrganisationList('RO');
|
|
84
|
+
|
|
85
|
+
expect(apiProvider.apiGet.mock.calls[0][0]).toContain("fields/Country eq 'RO'");
|
|
86
|
+
expect(result).toEqual([{ header: 'Org 1', content: 10 }]);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test('getMappingsList caches data after first call', async () => {
|
|
90
|
+
const { apiProvider, sharepointProvider } = loadModule();
|
|
91
|
+
apiProvider.getConfiguration.mockResolvedValue(baseConfig);
|
|
92
|
+
apiProvider.apiGet.mockResolvedValue({
|
|
93
|
+
graphClientMessage: {
|
|
94
|
+
value: [
|
|
95
|
+
{
|
|
96
|
+
fields: {
|
|
97
|
+
TeamURL: 'https://teams',
|
|
98
|
+
O365group: 'G',
|
|
99
|
+
O365GroupId: 'gid',
|
|
100
|
+
Membership: 'M',
|
|
101
|
+
Tag: 'T',
|
|
102
|
+
OtherMembership: false,
|
|
103
|
+
ManagementBoard: true,
|
|
104
|
+
EEAGroupLeads: [],
|
|
105
|
+
ETCManagers: [],
|
|
106
|
+
OfficialGroupName: 'Official',
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
],
|
|
110
|
+
},
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
const first = await sharepointProvider.getMappingsList();
|
|
114
|
+
const second = await sharepointProvider.getMappingsList();
|
|
115
|
+
|
|
116
|
+
expect(first).toEqual(second);
|
|
117
|
+
expect(apiProvider.apiGet).toHaveBeenCalledTimes(1);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
test('getCountries and getAvailableGroups read choice columns', async () => {
|
|
121
|
+
const { apiProvider, sharepointProvider } = loadModule();
|
|
122
|
+
apiProvider.getConfiguration.mockResolvedValue(baseConfig);
|
|
123
|
+
apiProvider.apiGet
|
|
124
|
+
.mockResolvedValueOnce({
|
|
125
|
+
graphClientMessage: {
|
|
126
|
+
value: [{ name: 'Country', choice: { choices: ['RO', 'DE'] } }],
|
|
127
|
+
},
|
|
128
|
+
})
|
|
129
|
+
.mockResolvedValueOnce({
|
|
130
|
+
graphClientMessage: {
|
|
131
|
+
value: [{ name: 'Membership', choice: { choices: ['Group A'] } }],
|
|
132
|
+
},
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
const countries = await sharepointProvider.getCountries();
|
|
136
|
+
const groups = await sharepointProvider.getAvailableGroups();
|
|
137
|
+
|
|
138
|
+
expect(countries).toEqual(['RO', 'DE']);
|
|
139
|
+
expect(groups).toEqual(['Group A']);
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
test('getSPUserByMail returns first profile when available', async () => {
|
|
143
|
+
const { apiProvider, sharepointProvider } = loadModule();
|
|
144
|
+
apiProvider.getConfiguration.mockResolvedValue(baseConfig);
|
|
145
|
+
apiProvider.apiGet.mockResolvedValue({
|
|
146
|
+
graphClientMessage: { value: [{ id: 1, fields: { Email: 'x@y' } }] },
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
const result = await sharepointProvider.getSPUserByMail("o'hara@example.org");
|
|
150
|
+
|
|
151
|
+
expect(apiProvider.apiGet.mock.calls[0][0]).toContain("o''hara%40example.org");
|
|
152
|
+
expect(result).toEqual({ id: 1, fields: { Email: 'x@y' } });
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
test('getConsultations maps consultation fields', async () => {
|
|
156
|
+
const { apiProvider, sharepointProvider } = loadModule();
|
|
157
|
+
apiProvider.getConfiguration.mockResolvedValue(baseConfig);
|
|
158
|
+
apiProvider.apiGet.mockResolvedValue({
|
|
159
|
+
graphClientMessage: {
|
|
160
|
+
value: [
|
|
161
|
+
{
|
|
162
|
+
fields: {
|
|
163
|
+
id: 1,
|
|
164
|
+
Title: 'Consultation',
|
|
165
|
+
ConsultationType: 'Consultation',
|
|
166
|
+
Description: 'Desc',
|
|
167
|
+
Startdate: '2024-01-01',
|
|
168
|
+
Closed: '2024-02-01',
|
|
169
|
+
Deadline: '2024-03-01',
|
|
170
|
+
Year: '2024',
|
|
171
|
+
LinktoFolder: 'https://folder',
|
|
172
|
+
Respondants: ['RO'],
|
|
173
|
+
Countries: ['RO'],
|
|
174
|
+
ConsulationmanagerLookupId: 9,
|
|
175
|
+
EionetGroups: ['G1'],
|
|
176
|
+
LinkToResults: 'https://results',
|
|
177
|
+
},
|
|
178
|
+
},
|
|
179
|
+
],
|
|
180
|
+
},
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
const result = await sharepointProvider.getConsultations('2024-01-01', 'RO');
|
|
184
|
+
|
|
185
|
+
expect(result).toHaveLength(1);
|
|
186
|
+
expect(result[0]).toEqual(
|
|
187
|
+
expect.objectContaining({
|
|
188
|
+
id: 1,
|
|
189
|
+
Title: 'Consultation',
|
|
190
|
+
HasUserCountryResponded: true,
|
|
191
|
+
}),
|
|
192
|
+
);
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
test('getParticipants handles pagination and maps fields', async () => {
|
|
196
|
+
const { apiProvider, sharepointProvider } = loadModule();
|
|
197
|
+
apiProvider.getConfiguration.mockResolvedValue(baseConfig);
|
|
198
|
+
apiProvider.apiGet
|
|
199
|
+
.mockResolvedValueOnce({
|
|
200
|
+
graphClientMessage: {
|
|
201
|
+
value: [
|
|
202
|
+
{
|
|
203
|
+
fields: {
|
|
204
|
+
id: 1,
|
|
205
|
+
MeetingtitleLookupId: 100,
|
|
206
|
+
Participantname: 'User 1',
|
|
207
|
+
EMail: 'u1@example.org',
|
|
208
|
+
Countries: 'RO',
|
|
209
|
+
Registered: true,
|
|
210
|
+
Participated: false,
|
|
211
|
+
},
|
|
212
|
+
},
|
|
213
|
+
],
|
|
214
|
+
'@odata.nextLink': 'next-link',
|
|
215
|
+
},
|
|
216
|
+
})
|
|
217
|
+
.mockResolvedValueOnce({
|
|
218
|
+
graphClientMessage: {
|
|
219
|
+
value: [
|
|
220
|
+
{
|
|
221
|
+
fields: {
|
|
222
|
+
id: 2,
|
|
223
|
+
MeetingtitleLookupId: 100,
|
|
224
|
+
Participantname: 'User 2',
|
|
225
|
+
EMail: 'u2@example.org',
|
|
226
|
+
Countries: 'RO',
|
|
227
|
+
Registered: false,
|
|
228
|
+
Participated: true,
|
|
229
|
+
},
|
|
230
|
+
},
|
|
231
|
+
],
|
|
232
|
+
},
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
const result = await sharepointProvider.getParticipants(100, 'RO');
|
|
236
|
+
|
|
237
|
+
expect(result).toHaveLength(2);
|
|
238
|
+
expect(result[0]).toEqual(
|
|
239
|
+
expect.objectContaining({
|
|
240
|
+
MeetingId: 100,
|
|
241
|
+
ParticipantName: 'User 1',
|
|
242
|
+
}),
|
|
243
|
+
);
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
test('getCurrentParticipant returns existing participant', async () => {
|
|
247
|
+
const { apiProvider, sharepointProvider } = loadModule();
|
|
248
|
+
apiProvider.getConfiguration.mockResolvedValue(baseConfig);
|
|
249
|
+
apiProvider.apiGet.mockResolvedValue({
|
|
250
|
+
graphClientMessage: {
|
|
251
|
+
value: [
|
|
252
|
+
{
|
|
253
|
+
fields: {
|
|
254
|
+
id: 1,
|
|
255
|
+
MeetingtitleLookupId: 100,
|
|
256
|
+
Participantname: 'User 1',
|
|
257
|
+
EMail: 'u1@example.org',
|
|
258
|
+
Countries: 'RO',
|
|
259
|
+
Registered: true,
|
|
260
|
+
Participated: false,
|
|
261
|
+
},
|
|
262
|
+
},
|
|
263
|
+
],
|
|
264
|
+
},
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
const event = { id: 100 };
|
|
268
|
+
const userInfo = { country: 'RO', mail: 'u1@example.org', givenName: 'U', surname: 'One' };
|
|
269
|
+
const result = await sharepointProvider.getCurrentParticipant(event, userInfo);
|
|
270
|
+
|
|
271
|
+
expect(result.Email).toBe('u1@example.org');
|
|
272
|
+
expect(result.Registered).toBe(true);
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
test('getCurrentParticipant builds default participant when user is missing', async () => {
|
|
276
|
+
const { apiProvider, sharepointProvider } = loadModule();
|
|
277
|
+
apiProvider.getConfiguration.mockResolvedValue(baseConfig);
|
|
278
|
+
apiProvider.apiGet.mockResolvedValue({
|
|
279
|
+
graphClientMessage: { value: [] },
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
const event = { id: 100 };
|
|
283
|
+
const userInfo = { country: 'RO', mail: 'u2@example.org', givenName: 'U', surname: 'Two' };
|
|
284
|
+
const result = await sharepointProvider.getCurrentParticipant(event, userInfo);
|
|
285
|
+
|
|
286
|
+
expect(result).toEqual(
|
|
287
|
+
expect.objectContaining({
|
|
288
|
+
MeetingId: 100,
|
|
289
|
+
ParticipantName: 'U Two',
|
|
290
|
+
Email: 'u2@example.org',
|
|
291
|
+
Registered: false,
|
|
292
|
+
}),
|
|
293
|
+
);
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
test('getGroups deduplicates and removes working groups', () => {
|
|
297
|
+
const { sharepointProvider } = loadModule();
|
|
298
|
+
const workingGroup = `${Constants.WorkingGroupPrefix}Air`;
|
|
299
|
+
const groups = sharepointProvider.getGroups(
|
|
300
|
+
[{ Membership: [workingGroup, 'Core Group'] }, { Membership: ['Core Group', 'Another'] }],
|
|
301
|
+
true,
|
|
302
|
+
);
|
|
303
|
+
|
|
304
|
+
expect(groups).toEqual(expect.arrayContaining(['Core Group', 'Another']));
|
|
305
|
+
expect(groups).not.toContain(workingGroup);
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
test('getPublications maps only records with date', async () => {
|
|
309
|
+
const { apiProvider, sharepointProvider } = loadModule();
|
|
310
|
+
apiProvider.getConfiguration.mockResolvedValue(baseConfig);
|
|
311
|
+
apiProvider.apiGet.mockResolvedValue({
|
|
312
|
+
graphClientMessage: {
|
|
313
|
+
value: [
|
|
314
|
+
{
|
|
315
|
+
fields: {
|
|
316
|
+
id: 1,
|
|
317
|
+
Title: 'Publication 1',
|
|
318
|
+
Item_x0020_type: 'News',
|
|
319
|
+
Status: 'Open',
|
|
320
|
+
Date_x0028_outpublic_x0029_: '2024-01-01',
|
|
321
|
+
},
|
|
322
|
+
},
|
|
323
|
+
{ fields: { id: 2, Title: 'No date' } },
|
|
324
|
+
],
|
|
325
|
+
},
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
const result = await sharepointProvider.getPublications();
|
|
329
|
+
|
|
330
|
+
expect(result).toHaveLength(1);
|
|
331
|
+
expect(result[0].Title).toBe('Publication 1');
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
test('getInvitedUsers maps memberships and organisation info', async () => {
|
|
335
|
+
const { apiProvider, sharepointProvider } = loadModule();
|
|
336
|
+
apiProvider.getConfiguration.mockResolvedValue(baseConfig);
|
|
337
|
+
apiProvider.apiGet
|
|
338
|
+
.mockResolvedValueOnce({
|
|
339
|
+
graphClientMessage: {
|
|
340
|
+
value: [{ id: 100, fields: { Title: 'Org 1' } }],
|
|
341
|
+
},
|
|
342
|
+
})
|
|
343
|
+
.mockResolvedValueOnce({
|
|
344
|
+
graphClientMessage: {
|
|
345
|
+
value: [
|
|
346
|
+
{
|
|
347
|
+
fields: {
|
|
348
|
+
id: 1,
|
|
349
|
+
Title: 'User 1',
|
|
350
|
+
Email: 'u1@example.org',
|
|
351
|
+
Membership: ['M1'],
|
|
352
|
+
OtherMemberships: ['M2'],
|
|
353
|
+
NFP: 'NFP',
|
|
354
|
+
Country: 'RO',
|
|
355
|
+
OrganisationLookupId: 100,
|
|
356
|
+
SignedIn: true,
|
|
357
|
+
},
|
|
358
|
+
},
|
|
359
|
+
],
|
|
360
|
+
},
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
const result = await sharepointProvider.getInvitedUsers('RO');
|
|
364
|
+
|
|
365
|
+
expect(apiProvider.apiGet.mock.calls[1][0]).toContain("fields/Country eq 'RO'");
|
|
366
|
+
expect(result).toEqual([
|
|
367
|
+
expect.objectContaining({
|
|
368
|
+
Title: 'User 1',
|
|
369
|
+
Organisation: 'Org 1',
|
|
370
|
+
AllMemberships: ['M1', 'M2', 'NFP'],
|
|
371
|
+
}),
|
|
372
|
+
]);
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
test('getObligations maps deadline and continuous records', async () => {
|
|
376
|
+
const { apiProvider, sharepointProvider } = loadModule();
|
|
377
|
+
apiProvider.getConfiguration.mockResolvedValue(baseConfig);
|
|
378
|
+
apiProvider.apiGet.mockResolvedValue({
|
|
379
|
+
graphClientMessage: {
|
|
380
|
+
value: [
|
|
381
|
+
{
|
|
382
|
+
fields: {
|
|
383
|
+
id: 1,
|
|
384
|
+
Title: 'Continuous',
|
|
385
|
+
IsEEACore: true,
|
|
386
|
+
},
|
|
387
|
+
},
|
|
388
|
+
{
|
|
389
|
+
fields: {
|
|
390
|
+
id: 2,
|
|
391
|
+
Title: 'Upcoming',
|
|
392
|
+
Deadline: '2999-01-01',
|
|
393
|
+
IsEEACore: false,
|
|
394
|
+
},
|
|
395
|
+
},
|
|
396
|
+
],
|
|
397
|
+
},
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
const result = await sharepointProvider.getObligations();
|
|
401
|
+
|
|
402
|
+
expect(result).toHaveLength(2);
|
|
403
|
+
expect(result.find((r) => r.id === 1).IsContinuous).toBe(true);
|
|
404
|
+
expect(result.find((r) => r.id === 2).IsUpcoming).toBe(true);
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
test('getMeetingManager caches AD user id', async () => {
|
|
408
|
+
const { apiProvider, sharepointProvider } = loadModule();
|
|
409
|
+
apiProvider.getConfiguration.mockResolvedValue(baseConfig);
|
|
410
|
+
apiProvider.apiGet
|
|
411
|
+
.mockResolvedValueOnce({
|
|
412
|
+
graphClientMessage: { fields: { EMail: 'user@example.org' } },
|
|
413
|
+
})
|
|
414
|
+
.mockResolvedValueOnce({
|
|
415
|
+
graphClientMessage: { value: [{ id: 'ad-user-id' }] },
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
const first = await sharepointProvider.getMeetingManager(99);
|
|
419
|
+
const second = await sharepointProvider.getMeetingManager(99);
|
|
420
|
+
|
|
421
|
+
expect(first).toBe('ad-user-id');
|
|
422
|
+
expect(second).toBe('ad-user-id');
|
|
423
|
+
expect(apiProvider.apiGet).toHaveBeenCalledTimes(2);
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
test('getADUserInfos returns user info with photo and tolerates photo error', async () => {
|
|
427
|
+
const { apiProvider, sharepointProvider } = loadModule();
|
|
428
|
+
apiProvider.getConfiguration.mockResolvedValue(baseConfig);
|
|
429
|
+
apiProvider.apiGet.mockImplementation(async (path) => {
|
|
430
|
+
if (path.includes('/User Information List/items/11')) {
|
|
431
|
+
return { graphClientMessage: { fields: { EMail: 'first@example.org' } } };
|
|
432
|
+
}
|
|
433
|
+
if (path.includes('/User Information List/items/22')) {
|
|
434
|
+
return { graphClientMessage: { fields: { EMail: 'second@example.org' } } };
|
|
435
|
+
}
|
|
436
|
+
if (path.includes("mail eq 'first%40example.org'")) {
|
|
437
|
+
return { graphClientMessage: { value: [{ id: 'first-ad-id', displayName: 'First' }] } };
|
|
438
|
+
}
|
|
439
|
+
if (path.includes("mail eq 'second%40example.org'")) {
|
|
440
|
+
return { graphClientMessage: { value: [{ id: 'second-ad-id', displayName: 'Second' }] } };
|
|
441
|
+
}
|
|
442
|
+
if (path.includes('/users/first-ad-id/photos/48x48/$value')) {
|
|
443
|
+
return { graphClientMessage: 'base64-first' };
|
|
444
|
+
}
|
|
445
|
+
if (path.includes('/users/second-ad-id/photos/48x48/$value')) {
|
|
446
|
+
throw new Error('photo missing');
|
|
447
|
+
}
|
|
448
|
+
return { graphClientMessage: {} };
|
|
449
|
+
});
|
|
450
|
+
|
|
451
|
+
const result = await sharepointProvider.getADUserInfos([11, 22]);
|
|
452
|
+
|
|
453
|
+
expect(result).toEqual([
|
|
454
|
+
expect.objectContaining({
|
|
455
|
+
id: 'first-ad-id',
|
|
456
|
+
lookupId: 11,
|
|
457
|
+
base64Photo: 'base64-first',
|
|
458
|
+
}),
|
|
459
|
+
expect.objectContaining({
|
|
460
|
+
id: 'second-ad-id',
|
|
461
|
+
lookupId: 22,
|
|
462
|
+
}),
|
|
463
|
+
]);
|
|
464
|
+
});
|
|
465
|
+
|
|
466
|
+
test('postParticipant sends external invitation email and returns graph message', async () => {
|
|
467
|
+
const { apiProvider, sharepointProvider } = loadModule();
|
|
468
|
+
apiProvider.getConfiguration.mockResolvedValue(baseConfig);
|
|
469
|
+
apiProvider.apiPost.mockResolvedValue({ graphClientMessage: { id: 'participant-id' } });
|
|
470
|
+
sendEmail.mockResolvedValue(undefined);
|
|
471
|
+
createIcs.mockReturnValue('ics-content');
|
|
472
|
+
|
|
473
|
+
const participant = {
|
|
474
|
+
MeetingId: 9,
|
|
475
|
+
ParticipantName: 'Ext User',
|
|
476
|
+
Email: 'ext@example.org',
|
|
477
|
+
Country: 'RO',
|
|
478
|
+
Registered: true,
|
|
479
|
+
Participated: false,
|
|
480
|
+
RegistrationDate: '2025-01-01',
|
|
481
|
+
PhysicalParticipation: false,
|
|
482
|
+
EEAReimbursementRequested: false,
|
|
483
|
+
CustomMeetingRequest: '',
|
|
484
|
+
IsInvitedByNFP: true,
|
|
485
|
+
};
|
|
486
|
+
const event = {
|
|
487
|
+
Title: 'Event A',
|
|
488
|
+
MeetingLink: 'https://join',
|
|
489
|
+
IsOnline: true,
|
|
490
|
+
IsOnlineOrHybrid: true,
|
|
491
|
+
};
|
|
492
|
+
|
|
493
|
+
const result = await sharepointProvider.postParticipant(participant, event);
|
|
494
|
+
|
|
495
|
+
expect(apiProvider.apiPost).toHaveBeenCalledWith(
|
|
496
|
+
'/sites/site-id/lists/participants-list-id/items',
|
|
497
|
+
expect.objectContaining({
|
|
498
|
+
fields: expect.objectContaining({
|
|
499
|
+
Participantname: 'Ext User',
|
|
500
|
+
IsInvitedByNFP: true,
|
|
501
|
+
}),
|
|
502
|
+
}),
|
|
503
|
+
);
|
|
504
|
+
expect(createIcs).toHaveBeenCalled();
|
|
505
|
+
expect(sendEmail).toHaveBeenCalledWith(
|
|
506
|
+
'Invite online Event A',
|
|
507
|
+
'Invite body Event A https://join',
|
|
508
|
+
['ext@example.org'],
|
|
509
|
+
'ics-content',
|
|
510
|
+
);
|
|
511
|
+
expect(result).toEqual({ id: 'participant-id' });
|
|
512
|
+
});
|
|
513
|
+
|
|
514
|
+
test('patchParticipant sends approval email with attachment for approved online event', async () => {
|
|
515
|
+
const { apiProvider, sharepointProvider } = loadModule();
|
|
516
|
+
apiProvider.getConfiguration.mockResolvedValue(baseConfig);
|
|
517
|
+
apiProvider.apiPatch.mockResolvedValue({});
|
|
518
|
+
sendEmail.mockResolvedValue(undefined);
|
|
519
|
+
createIcs.mockReturnValue('ics-approval');
|
|
520
|
+
|
|
521
|
+
const participant = {
|
|
522
|
+
id: 7,
|
|
523
|
+
Email: 'user@example.org',
|
|
524
|
+
Registered: true,
|
|
525
|
+
Participated: false,
|
|
526
|
+
PhysicalParticipation: false,
|
|
527
|
+
EEAReimbursementRequested: false,
|
|
528
|
+
CustomMeetingRequest: '',
|
|
529
|
+
NFPApproved: 'Approved',
|
|
530
|
+
};
|
|
531
|
+
const event = {
|
|
532
|
+
Title: 'Event B',
|
|
533
|
+
MeetingLink: 'https://join',
|
|
534
|
+
MeetingType: 'Online',
|
|
535
|
+
IsOnlineOrHybrid: true,
|
|
536
|
+
};
|
|
537
|
+
|
|
538
|
+
const ok = await sharepointProvider.patchParticipant(participant, event, true);
|
|
539
|
+
|
|
540
|
+
expect(ok).toBe(true);
|
|
541
|
+
expect(sendEmail).toHaveBeenCalledWith(
|
|
542
|
+
'User online Event B',
|
|
543
|
+
'Accepted Event B https://join',
|
|
544
|
+
['user@example.org'],
|
|
545
|
+
'ics-approval',
|
|
546
|
+
);
|
|
547
|
+
});
|
|
548
|
+
|
|
549
|
+
test('deleteParticipant returns false when delete fails', async () => {
|
|
550
|
+
const { apiProvider, sharepointProvider } = loadModule();
|
|
551
|
+
apiProvider.getConfiguration.mockResolvedValue(baseConfig);
|
|
552
|
+
apiProvider.apiDelete.mockRejectedValue(new Error('delete failed'));
|
|
553
|
+
|
|
554
|
+
const result = await sharepointProvider.deleteParticipant({ id: 1 });
|
|
555
|
+
|
|
556
|
+
expect(result).toBe(false);
|
|
557
|
+
});
|
|
558
|
+
|
|
559
|
+
test('postRating posts new rating and updates participant as voted', async () => {
|
|
560
|
+
const { apiProvider, sharepointProvider } = loadModule();
|
|
561
|
+
apiProvider.getConfiguration.mockResolvedValue(baseConfig);
|
|
562
|
+
apiProvider.apiGet.mockResolvedValue({
|
|
563
|
+
graphClientMessage: { value: [] },
|
|
564
|
+
});
|
|
565
|
+
apiProvider.apiPost.mockResolvedValue({});
|
|
566
|
+
apiProvider.apiPatch.mockResolvedValue({});
|
|
567
|
+
apiProvider.logInfo.mockResolvedValue(undefined);
|
|
568
|
+
|
|
569
|
+
const result = await sharepointProvider.postRating({ id: 99, Title: 'Event C' }, { id: 8 }, 4);
|
|
570
|
+
|
|
571
|
+
expect(result).toBe(true);
|
|
572
|
+
expect(apiProvider.apiPost).toHaveBeenCalledWith(
|
|
573
|
+
'/sites/site-id/lists/meeting-rating-list-id/items',
|
|
574
|
+
expect.objectContaining({
|
|
575
|
+
fields: expect.objectContaining({
|
|
576
|
+
EventLookupId: 99,
|
|
577
|
+
Responses: 1,
|
|
578
|
+
Rating: 4,
|
|
579
|
+
}),
|
|
580
|
+
}),
|
|
581
|
+
);
|
|
582
|
+
expect(apiProvider.apiPatch).toHaveBeenCalledWith(
|
|
583
|
+
'/sites/site-id/lists/participants-list-id/items/8',
|
|
584
|
+
{ fields: { Voted: true } },
|
|
585
|
+
);
|
|
586
|
+
expect(apiProvider.logInfo).toHaveBeenCalled();
|
|
587
|
+
});
|
|
588
|
+
|
|
589
|
+
test('postRating retries when patch gets 412 and then succeeds', async () => {
|
|
590
|
+
const { apiProvider, sharepointProvider } = loadModule();
|
|
591
|
+
apiProvider.getConfiguration.mockResolvedValue(baseConfig);
|
|
592
|
+
apiProvider.apiGet
|
|
593
|
+
.mockResolvedValueOnce({
|
|
594
|
+
graphClientMessage: {
|
|
595
|
+
value: [{ id: 5, eTag: 'v1', fields: { Responses: 1, Rating: 2 } }],
|
|
596
|
+
},
|
|
597
|
+
})
|
|
598
|
+
.mockResolvedValueOnce({
|
|
599
|
+
graphClientMessage: {
|
|
600
|
+
value: [{ id: 5, eTag: 'v2', fields: { Responses: 2, Rating: 3 } }],
|
|
601
|
+
},
|
|
602
|
+
});
|
|
603
|
+
apiProvider.apiPatch
|
|
604
|
+
.mockRejectedValueOnce({ response: { status: 412 } })
|
|
605
|
+
.mockResolvedValueOnce({})
|
|
606
|
+
.mockResolvedValueOnce({});
|
|
607
|
+
apiProvider.logInfo.mockResolvedValue(undefined);
|
|
608
|
+
|
|
609
|
+
const result = await sharepointProvider.postRating({ id: 77, Title: 'Event D' }, { id: 9 }, 5);
|
|
610
|
+
|
|
611
|
+
expect(result).toBe(true);
|
|
612
|
+
expect(apiProvider.apiPatch).toHaveBeenNthCalledWith(
|
|
613
|
+
1,
|
|
614
|
+
'/sites/site-id/lists/meeting-rating-list-id/items/5',
|
|
615
|
+
{ fields: { Responses: 2, Rating: 7 } },
|
|
616
|
+
'v1',
|
|
617
|
+
);
|
|
618
|
+
expect(apiProvider.apiPatch).toHaveBeenNthCalledWith(
|
|
619
|
+
2,
|
|
620
|
+
'/sites/site-id/lists/meeting-rating-list-id/items/5',
|
|
621
|
+
{ fields: { Responses: 3, Rating: 8 } },
|
|
622
|
+
'v2',
|
|
623
|
+
);
|
|
624
|
+
});
|
|
625
|
+
|
|
626
|
+
test('postRating returns false when participant vote update fails', async () => {
|
|
627
|
+
const { apiProvider, sharepointProvider } = loadModule();
|
|
628
|
+
apiProvider.getConfiguration.mockResolvedValue(baseConfig);
|
|
629
|
+
apiProvider.apiGet.mockResolvedValue({
|
|
630
|
+
graphClientMessage: { value: [] },
|
|
631
|
+
});
|
|
632
|
+
apiProvider.apiPost.mockResolvedValue({});
|
|
633
|
+
apiProvider.apiPatch.mockRejectedValueOnce(new Error('patch participant failed'));
|
|
634
|
+
|
|
635
|
+
const result = await sharepointProvider.postRating(
|
|
636
|
+
{ id: 101, Title: 'Event E' },
|
|
637
|
+
{ id: 10 },
|
|
638
|
+
2,
|
|
639
|
+
);
|
|
640
|
+
|
|
641
|
+
expect(result).toBe(false);
|
|
642
|
+
});
|
|
643
|
+
|
|
644
|
+
test('getCountryCodeMappingsList caches mapping list', async () => {
|
|
645
|
+
const { apiProvider, sharepointProvider } = loadModule();
|
|
646
|
+
apiProvider.getConfiguration.mockResolvedValue(baseConfig);
|
|
647
|
+
apiProvider.apiGet.mockResolvedValue({
|
|
648
|
+
graphClientMessage: {
|
|
649
|
+
value: [
|
|
650
|
+
{
|
|
651
|
+
fields: {
|
|
652
|
+
Title: 'RO',
|
|
653
|
+
CountryName: 'Romania',
|
|
654
|
+
CDO: [],
|
|
655
|
+
TeamMember: [],
|
|
656
|
+
},
|
|
657
|
+
},
|
|
658
|
+
],
|
|
659
|
+
},
|
|
660
|
+
});
|
|
661
|
+
|
|
662
|
+
const first = await sharepointProvider.getCountryCodeMappingsList();
|
|
663
|
+
const second = await sharepointProvider.getCountryCodeMappingsList();
|
|
664
|
+
|
|
665
|
+
expect(first).toEqual(second);
|
|
666
|
+
expect(first[0].CountryCode).toBe('RO');
|
|
667
|
+
expect(apiProvider.apiGet).toHaveBeenCalledTimes(1);
|
|
668
|
+
});
|
|
669
|
+
});
|
|
@@ -1,9 +1,41 @@
|
|
|
1
1
|
const validator = require('./validator');
|
|
2
2
|
|
|
3
|
-
test('validateName', () => {
|
|
3
|
+
test('validateName returns length error for single character', () => {
|
|
4
4
|
expect(validator.validateName('t')).toBe('Please enter at least 2 characters!');
|
|
5
5
|
});
|
|
6
6
|
|
|
7
|
-
test('
|
|
7
|
+
test('validateName returns mandatory error for empty value', () => {
|
|
8
|
+
expect(validator.validateName('')).toBe('Please fill out this field!');
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
test('validateName passes for valid value', () => {
|
|
12
|
+
expect(validator.validateName('test')).toBe(undefined);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
test('validateMandatoryField passes for non-empty string', () => {
|
|
8
16
|
expect(validator.validateMandatoryField('test')).toBe(undefined);
|
|
9
17
|
});
|
|
18
|
+
|
|
19
|
+
test('validateMandatoryField returns mandatory error for empty string', () => {
|
|
20
|
+
expect(validator.validateMandatoryField('')).toBe('Please fill out this field!');
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
test('validateMandatoryField returns mandatory error for empty array', () => {
|
|
24
|
+
expect(validator.validateMandatoryField([])).toBe('Please fill out this field!');
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
test('validateMandatoryField passes for non-empty array', () => {
|
|
28
|
+
expect(validator.validateMandatoryField(['a'])).toBe(undefined);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test('validatePhone returns format error for invalid input', () => {
|
|
32
|
+
expect(validator.validatePhone('abc')).toBe('Only numbers and characters +, -, . are allowed.');
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test('validatePhone passes for valid numeric input', () => {
|
|
36
|
+
expect(validator.validatePhone('+40 123-456.78')).toBe(undefined);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test('validatePhone passes for empty input', () => {
|
|
40
|
+
expect(validator.validatePhone('')).toBe(undefined);
|
|
41
|
+
});
|