Merge branch '21621-io-panel-json-tab-copy' into main. Closes #21621
[arvados.git] / services / workbench2 / cypress / e2e / user-profile.cy.js
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 describe('User profile tests', function() {
6     let activeUser;
7     let adminUser;
8     const roleGroupName = `Test role group (${Math.floor(999999 * Math.random())})`;
9     const projectGroupName = `Test project group (${Math.floor(999999 * Math.random())})`;
10
11     before(function() {
12         // Only set up common users once. These aren't set up as aliases because
13         // aliases are cleaned up after every test. Also it doesn't make sense
14         // to set the same users on beforeEach() over and over again, so we
15         // separate a little from Cypress' 'Best Practices' here.
16         cy.getUser('admin', 'Admin', 'User', true, true)
17             .as('adminUser').then(function() {
18                 adminUser = this.adminUser;
19             }
20         );
21         cy.getUser('user', 'Active', 'User', false, true)
22             .as('activeUser').then(function() {
23                 activeUser = this.activeUser;
24             }
25         );
26     });
27
28     function assertProfileValues({
29         firstName,
30         lastName,
31         email,
32         username,
33         org,
34         org_email,
35         role,
36         website,
37     }) {
38         cy.get('[data-cy=profile-form] input[name="firstName"]').invoke('val').should('equal', firstName);
39         cy.get('[data-cy=profile-form] input[name="lastName"]').invoke('val').should('equal', lastName);
40         cy.get('[data-cy=profile-form] [data-cy=email] [data-cy=value]').contains(email);
41         cy.get('[data-cy=profile-form] [data-cy=username] [data-cy=value]').contains(username);
42
43         cy.get('[data-cy=profile-form] input[name="prefs.profile.organization"]').invoke('val').should('equal', org);
44         cy.get('[data-cy=profile-form] input[name="prefs.profile.organization_email"]').invoke('val').should('equal', org_email);
45         cy.get('[data-cy=profile-form] select[name="prefs.profile.role"]').invoke('val').should('equal', role);
46         cy.get('[data-cy=profile-form] input[name="prefs.profile.website_url"]').invoke('val').should('equal', website);
47     }
48
49     function enterProfileValues({
50         org,
51         org_email,
52         role,
53         website,
54     }) {
55         cy.get('[data-cy=profile-form] input[name="prefs.profile.organization"]').clear();
56         if (org) {
57             cy.get('[data-cy=profile-form] input[name="prefs.profile.organization"]').type(org);
58         }
59         cy.get('[data-cy=profile-form] input[name="prefs.profile.organization_email"]').clear();
60         if (org_email) {
61             cy.get('[data-cy=profile-form] input[name="prefs.profile.organization_email"]').type(org_email);
62         }
63         cy.get('[data-cy=profile-form] select[name="prefs.profile.role"]').select(role);
64         cy.get('[data-cy=profile-form] input[name="prefs.profile.website_url"]').clear();
65         if (website) {
66             cy.get('[data-cy=profile-form] input[name="prefs.profile.website_url"]').type(website);
67         }
68     }
69
70     function assertContextMenuItems({
71         account,
72         activate,
73         deactivate,
74         login,
75         setup
76     }) {
77         cy.get('[data-cy=user-profile-panel-options-btn]').click();
78         cy.get('[data-cy=context-menu]').within(() => {
79             cy.get('[role=button]').contains('API Details');
80
81             cy.get('[role=button]').should(account ? 'contain' : 'not.contain', 'Account Settings');
82             cy.get('[role=button]').should(activate ? 'contain' : 'not.contain', 'Activate user');
83             cy.get('[role=button]').should(deactivate ? 'contain' : 'not.contain', 'Deactivate user');
84             cy.get('[role=button]').should(login ? 'contain' : 'not.contain', 'Login as user');
85             cy.get('[role=button]').should(setup ? 'contain' : 'not.contain', 'Setup user');
86         });
87         cy.get('div[role=presentation]').click();
88     }
89
90     beforeEach(function() {
91         cy.updateResource(adminUser.token, 'users', adminUser.user.uuid, {
92             prefs: {
93                 profile: {
94                     organization: '',
95                     organization_email: '',
96                     role: '',
97                     website_url: '',
98                 },
99             },
100         });
101         cy.updateResource(adminUser.token, 'users', activeUser.user.uuid, {
102             prefs: {
103                 profile: {
104                     organization: '',
105                     organization_email: '',
106                     role: '',
107                     website_url: '',
108                 },
109             },
110         });
111     });
112
113     it('non-admin can edit own profile', function() {
114         cy.loginAs(activeUser);
115
116         cy.get('header button[title="Account Management"]').click();
117         cy.get('#account-menu').contains('My account').click();
118
119         // Admin actions should be hidden, no account menu
120         assertContextMenuItems({
121             account: false,
122             activate: false,
123             deactivate: false,
124             login: false,
125             setup: false,
126         });
127
128         // Check initial values
129         assertProfileValues({
130             firstName: 'Active',
131             lastName: 'User',
132             email: 'user@example.local',
133             username: 'user',
134             org: '',
135             org_email: '',
136             role: '',
137             website: '',
138         });
139
140         // Change values
141         enterProfileValues({
142             org: 'Org name',
143             org_email: 'email@example.com',
144             role: 'Data Scientist',
145             website: 'example.com',
146         });
147
148         cy.get('[data-cy=profile-form] button[type="submit"]').should('not.be.disabled');
149
150         // Submit
151         cy.get('[data-cy=profile-form] button[type="submit"]').click();
152
153         // Check new values
154         assertProfileValues({
155             firstName: 'Active',
156             lastName: 'User',
157             email: 'user@example.local',
158             username: 'user',
159             org: 'Org name',
160             org_email: 'email@example.com',
161             role: 'Data Scientist',
162             website: 'example.com',
163         });
164
165         // if it worked, the save button should be disabled.
166         cy.get('[data-cy=profile-form] button[type="submit"]').should('be.disabled');
167     });
168
169     it('non-admin cannot edit other profile', function() {
170         cy.loginAs(activeUser);
171         cy.goToPath('/user/' + adminUser.user.uuid);
172
173         assertProfileValues({
174             firstName: 'Admin',
175             lastName: 'User',
176             email: 'admin@example.local',
177             username: 'admin',
178             org: '',
179             org_email: '',
180             role: '',
181             website: '',
182         });
183
184         // Inputs should be disabled
185         cy.get('[data-cy=profile-form] input[name="prefs.profile.organization"]').should('be.disabled');
186         cy.get('[data-cy=profile-form] input[name="prefs.profile.organization_email"]').should('be.disabled');
187         cy.get('[data-cy=profile-form] select[name="prefs.profile.role"]').should('be.disabled');
188         cy.get('[data-cy=profile-form] input[name="prefs.profile.website_url"]').should('be.disabled');
189
190         // Submit should be disabled
191         cy.get('[data-cy=profile-form] button[type="submit"]').should('be.disabled');
192
193         // Admin actions should be hidden, no account menu
194         assertContextMenuItems({
195             account: false,
196             activate: false,
197             deactivate: false,
198             login: false,
199             setup: false,
200         });
201     });
202
203     it('admin can edit own profile', function() {
204         cy.loginAs(adminUser);
205
206         cy.get('header button[title="Account Management"]').click();
207         cy.get('#account-menu').contains('My account').click();
208
209         // Admin actions should be visible, no account menu
210         assertContextMenuItems({
211             account: false,
212             activate: false,
213             deactivate: true,
214             login: false,
215             setup: false,
216         });
217
218         // Check initial values
219         assertProfileValues({
220             firstName: 'Admin',
221             lastName: 'User',
222             email: 'admin@example.local',
223             username: 'admin',
224             org: '',
225             org_email: '',
226             role: '',
227             website: '',
228         });
229
230         // Change values
231         enterProfileValues({
232             org: 'Admin org name',
233             org_email: 'admin@example.com',
234             role: 'Researcher',
235             website: 'admin.local',
236         });
237         cy.get('[data-cy=profile-form] button[type="submit"]').click();
238
239         // Check new values
240         assertProfileValues({
241             firstName: 'Admin',
242             lastName: 'User',
243             email: 'admin@example.local',
244             username: 'admin',
245             org: 'Admin org name',
246             org_email: 'admin@example.com',
247             role: 'Researcher',
248             website: 'admin.local',
249         });
250     });
251
252     it('admin can edit other profile', function() {
253         cy.loginAs(adminUser);
254         cy.goToPath('/user/' + activeUser.user.uuid);
255
256         // Check new values
257         assertProfileValues({
258             firstName: 'Active',
259             lastName: 'User',
260             email: 'user@example.local',
261             username: 'user',
262             org: '',
263             org_email: '',
264             role: '',
265             website: '',
266         });
267
268         enterProfileValues({
269             org: 'Changed org name',
270             org_email: 'changed@example.com',
271             role: 'Researcher',
272             website: 'changed.local',
273         });
274         cy.get('[data-cy=profile-form] button[type="submit"]').click();
275
276         // Check new values
277         assertProfileValues({
278             firstName: 'Active',
279             lastName: 'User',
280             email: 'user@example.local',
281             username: 'user',
282             org: 'Changed org name',
283             org_email: 'changed@example.com',
284             role: 'Researcher',
285             website: 'changed.local',
286         });
287
288         // Admin actions should be visible, no account menu
289         assertContextMenuItems({
290             account: false,
291             activate: false,
292             deactivate: true,
293             login: true,
294             setup: false,
295         });
296     });
297
298     it('displays role groups on user profile', function() {
299         cy.loginAs(adminUser);
300
301         cy.createGroup(adminUser.token, {
302             name: roleGroupName,
303             group_class: 'role',
304         }).as('roleGroup').then(function() {
305             cy.createLink(adminUser.token, {
306                 name: 'can_write',
307                 link_class: 'permission',
308                 head_uuid: this.roleGroup.uuid,
309                 tail_uuid: adminUser.user.uuid
310             });
311             cy.createLink(adminUser.token, {
312                 name: 'can_write',
313                 link_class: 'permission',
314                 head_uuid: this.roleGroup.uuid,
315                 tail_uuid: activeUser.user.uuid
316             });
317         });
318
319         cy.createGroup(adminUser.token, {
320             name: projectGroupName,
321             group_class: 'project',
322         }).as('projectGroup').then(function() {
323             cy.createLink(adminUser.token, {
324                 name: 'can_write',
325                 link_class: 'permission',
326                 head_uuid: this.projectGroup.uuid,
327                 tail_uuid: adminUser.user.uuid
328             });
329             cy.createLink(adminUser.token, {
330                 name: 'can_write',
331                 link_class: 'permission',
332                 head_uuid: this.projectGroup.uuid,
333                 tail_uuid: activeUser.user.uuid
334             });
335         });
336
337         cy.goToPath('/user/' + activeUser.user.uuid);
338         cy.get('div [role="tab"]').contains('GROUPS').click();
339         cy.get('[data-cy=user-profile-groups-data-explorer]').contains(roleGroupName);
340         cy.get('[data-cy=user-profile-groups-data-explorer]').should('not.contain', projectGroupName);
341
342         cy.goToPath('/user/' + adminUser.user.uuid);
343         cy.get('div [role="tab"]').contains('GROUPS').click();
344         cy.get('[data-cy=user-profile-groups-data-explorer]').contains(roleGroupName);
345         cy.get('[data-cy=user-profile-groups-data-explorer]').should('not.contain', projectGroupName);
346     });
347
348     it('allows performing admin functions', function() {
349         cy.loginAs(adminUser);
350         cy.goToPath('/user/' + activeUser.user.uuid);
351
352         // Check that user is active
353         cy.get('[data-cy=account-status]').contains('Active');
354         cy.get('div [role="tab"]').contains('GROUPS').click();
355         cy.get('[data-cy=user-profile-groups-data-explorer]').should('contain', 'All users');
356         cy.get('div [role="tab"]').contains('PROFILE').click();
357         assertContextMenuItems({
358             account: false,
359             activate: false,
360             deactivate: true,
361             login: true,
362             setup: false,
363         });
364
365         // Deactivate user
366         cy.get('[data-cy=user-profile-panel-options-btn]').click();
367         cy.get('[data-cy=context-menu]').contains('Deactivate user').click();
368         cy.get('[data-cy=confirmation-dialog-ok-btn]').click();
369
370         // Check that user is deactivated
371         cy.get('[data-cy=account-status]').contains('Inactive');
372         cy.get('div [role="tab"]').contains('GROUPS').click();
373         cy.get('[data-cy=user-profile-groups-data-explorer]').should('not.contain', 'All users');
374         cy.get('div [role="tab"]').contains('PROFILE').click();
375         assertContextMenuItems({
376             account: false,
377             activate: true,
378             deactivate: false,
379             login: true,
380             setup: true,
381         });
382
383         // Setup user
384         cy.get('[data-cy=user-profile-panel-options-btn]').click();
385         cy.get('[data-cy=context-menu]').contains('Setup user').click();
386         cy.get('[data-cy=confirmation-dialog-ok-btn]').click();
387
388         // Check that user is setup
389         cy.get('[data-cy=account-status]').contains('Setup');
390         cy.get('div [role="tab"]').contains('GROUPS').click();
391         cy.get('[data-cy=user-profile-groups-data-explorer]').should('contain', 'All users');
392         cy.get('div [role="tab"]').contains('PROFILE').click();
393         assertContextMenuItems({
394             account: false,
395             activate: true,
396             deactivate: true,
397             login: true,
398             setup: false,
399         });
400
401         // Activate user
402         cy.get('[data-cy=user-profile-panel-options-btn]').click();
403         cy.get('[data-cy=context-menu]').contains('Activate user').click();
404         cy.get('[data-cy=confirmation-dialog-ok-btn]').click();
405
406         // Check that user is active
407         cy.get('[data-cy=account-status]').contains('Active');
408         cy.get('div [role="tab"]').contains('GROUPS').click();
409         cy.get('[data-cy=user-profile-groups-data-explorer]').should('contain', 'All users');
410         cy.get('div [role="tab"]').contains('PROFILE').click();
411         assertContextMenuItems({
412             account: false,
413             activate: false,
414             deactivate: true,
415             login: true,
416             setup: false,
417         });
418
419         // Deactivate and activate user skipping setup
420         cy.get('[data-cy=user-profile-panel-options-btn]').click();
421         cy.get('[data-cy=context-menu]').contains('Deactivate user').click();
422         cy.get('[data-cy=confirmation-dialog-ok-btn]').click();
423         // Check
424         cy.get('[data-cy=account-status]').contains('Inactive');
425         cy.get('div [role="tab"]').contains('GROUPS').click();
426         cy.get('[data-cy=user-profile-groups-data-explorer]').should('not.contain', 'All users');
427         cy.get('div [role="tab"]').contains('PROFILE').click();
428         assertContextMenuItems({
429             account: false,
430             activate: true,
431             deactivate: false,
432             login: true,
433             setup: true,
434         });
435         // reactivate
436         cy.get('[data-cy=user-profile-panel-options-btn]').click();
437         cy.get('[data-cy=context-menu]').contains('Activate user').click();
438         cy.get('[data-cy=confirmation-dialog-ok-btn]').click();
439
440         // Check that user is active
441         cy.get('[data-cy=account-status]').contains('Active');
442         cy.get('div [role="tab"]').contains('GROUPS').click();
443         cy.get('[data-cy=user-profile-groups-data-explorer]').should('contain', 'All users');
444         cy.get('div [role="tab"]').contains('PROFILE').click();
445         assertContextMenuItems({
446             account: false,
447             activate: false,
448             deactivate: true,
449             login: true,
450             setup: false,
451         });
452     });
453
454 });