18559: Add user profile tests.
authorStephen Smith <stephen@curii.com>
Tue, 15 Mar 2022 18:02:17 +0000 (14:02 -0400)
committerStephen Smith <stephen@curii.com>
Tue, 15 Mar 2022 18:02:17 +0000 (14:02 -0400)
Arvados-DCO-1.1-Signed-off-by: Stephen Smith <stephen@curii.com>

cypress/integration/user-profile.spec.js [new file with mode: 0644]
src/views/user-profile-panel/user-profile-panel-root.tsx

diff --git a/cypress/integration/user-profile.spec.js b/cypress/integration/user-profile.spec.js
new file mode 100644 (file)
index 0000000..325f38c
--- /dev/null
@@ -0,0 +1,294 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+describe('User profile tests', function() {
+    let activeUser;
+    let adminUser;
+    const roleGroupName = `Test role group (${Math.floor(999999 * Math.random())})`;
+    const projectGroupName = `Test project group (${Math.floor(999999 * Math.random())})`;
+
+    before(function() {
+        // Only set up common users once. These aren't set up as aliases because
+        // aliases are cleaned up after every test. Also it doesn't make sense
+        // to set the same users on beforeEach() over and over again, so we
+        // separate a little from Cypress' 'Best Practices' here.
+        cy.getUser('admin', 'Admin', 'User', true, true)
+            .as('adminUser').then(function() {
+                adminUser = this.adminUser;
+            }
+        );
+        cy.getUser('user', 'Active', 'User', false, true)
+            .as('activeUser').then(function() {
+                activeUser = this.activeUser;
+            }
+        );
+    });
+
+    function assertProfileValues({
+        firstName,
+        lastName,
+        email,
+        username,
+        org,
+        org_email,
+        role,
+        website,
+    }) {
+        cy.get('[data-cy=profile-form] [data-cy=firstName] [data-cy=value]').contains(firstName);
+        cy.get('[data-cy=profile-form] [data-cy=lastName] [data-cy=value]').contains(lastName);
+        cy.get('[data-cy=profile-form] [data-cy=email] [data-cy=value]').contains(email);
+        cy.get('[data-cy=profile-form] [data-cy=username] [data-cy=value]').contains(username);
+
+        cy.get('[data-cy=profile-form] input[name="prefs.profile.organization"]').invoke('val').should('equal', org);
+        cy.get('[data-cy=profile-form] input[name="prefs.profile.organization_email"]').invoke('val').should('equal', org_email);
+        cy.get('[data-cy=profile-form] select[name="prefs.profile.role"]').invoke('val').should('equal', role);
+        cy.get('[data-cy=profile-form] input[name="prefs.profile.website_url"]').invoke('val').should('equal', website);
+    }
+
+    function enterProfileValues({
+        org,
+        org_email,
+        role,
+        website,
+    }) {
+        cy.get('[data-cy=profile-form] input[name="prefs.profile.organization"]').clear();
+        if (org) {
+            cy.get('[data-cy=profile-form] input[name="prefs.profile.organization"]').type(org);
+        }
+        cy.get('[data-cy=profile-form] input[name="prefs.profile.organization_email"]').clear();
+        if (org_email) {
+            cy.get('[data-cy=profile-form] input[name="prefs.profile.organization_email"]').type(org_email);
+        }
+        cy.get('[data-cy=profile-form] select[name="prefs.profile.role"]').select(role);
+        cy.get('[data-cy=profile-form] input[name="prefs.profile.website_url"]').clear();
+        if (website) {
+            cy.get('[data-cy=profile-form] input[name="prefs.profile.website_url"]').type(website);
+        }
+    }
+
+    beforeEach(function() {
+        cy.loginAs(adminUser);
+        cy.goToPath('/my-account');
+        enterProfileValues({
+            org: '',
+            org_email: '',
+            role: '',
+            website: '',
+        });
+        cy.get('[data-cy=profile-form] button[type="submit"]').click({force: true});
+
+        cy.goToPath('/user/' + activeUser.user.uuid);
+        enterProfileValues({
+            org: '',
+            org_email: '',
+            role: '',
+            website: '',
+        });
+        cy.get('[data-cy=profile-form] button[type="submit"]').click({force: true});
+    });
+
+    it('non-admin can edit own profile', function() {
+        cy.loginAs(activeUser);
+
+        cy.get('header button[title="Account Management"]').click();
+        cy.get('#account-menu').contains('My account').click();
+
+        // Admin tab should be hidden
+        cy.get('div [role="tab"]').should('not.contain', 'ADMIN');
+
+        // Check initial values
+        assertProfileValues({
+            firstName: 'Active',
+            lastName: 'User',
+            email: 'user@example.local',
+            username: 'user',
+            org: '',
+            org_email: '',
+            role: '',
+            website: '',
+        });
+
+        // Change values
+        enterProfileValues({
+            org: 'Org name',
+            org_email: 'email@example.com',
+            role: 'Data Scientist',
+            website: 'example.com',
+        });
+
+        // Submit
+        cy.get('[data-cy=profile-form] button[type="submit"]').click();
+
+        // Check new values
+        assertProfileValues({
+            firstName: 'Active',
+            lastName: 'User',
+            email: 'user@example.local',
+            username: 'user',
+            org: 'Org name',
+            org_email: 'email@example.com',
+            role: 'Data Scientist',
+            website: 'example.com',
+        });
+    });
+
+    it('non-admin cannot edit other profile', function() {
+        cy.loginAs(activeUser);
+        cy.goToPath('/user/' + adminUser.user.uuid);
+
+        assertProfileValues({
+            firstName: 'Admin',
+            lastName: 'User',
+            email: 'admin@example.local',
+            username: 'admin',
+            org: '',
+            org_email: '',
+            role: '',
+            website: '',
+        });
+
+        // Inputs should be disabled
+        cy.get('[data-cy=profile-form] input[name="prefs.profile.organization"]').should('be.disabled');
+        cy.get('[data-cy=profile-form] input[name="prefs.profile.organization_email"]').should('be.disabled');
+        cy.get('[data-cy=profile-form] select[name="prefs.profile.role"]').should('be.disabled');
+        cy.get('[data-cy=profile-form] input[name="prefs.profile.website_url"]').should('be.disabled');
+
+        // Submit should be disabled
+        cy.get('[data-cy=profile-form] button[type="submit"]').should('be.disabled');
+
+        // Admin tab should be hidden
+        cy.get('div [role="tab"]').should('not.contain', 'ADMIN');
+    });
+
+    it('admin can edit own profile', function() {
+        cy.loginAs(adminUser);
+
+        cy.get('header button[title="Account Management"]').click();
+        cy.get('#account-menu').contains('My account').click();
+
+        // Admin tab should be visible
+        cy.get('div [role="tab"]').should('contain', 'ADMIN');
+
+        // Check initial values
+        assertProfileValues({
+            firstName: 'Admin',
+            lastName: 'User',
+            email: 'admin@example.local',
+            username: 'admin',
+            org: '',
+            org_email: '',
+            role: '',
+            website: '',
+        });
+
+        // Change values
+        enterProfileValues({
+            org: 'Admin org name',
+            org_email: 'admin@example.com',
+            role: 'Researcher',
+            website: 'admin.local',
+        });
+        cy.get('[data-cy=profile-form] button[type="submit"]').click();
+
+        // Check new values
+        assertProfileValues({
+            firstName: 'Admin',
+            lastName: 'User',
+            email: 'admin@example.local',
+            username: 'admin',
+            org: 'Admin org name',
+            org_email: 'admin@example.com',
+            role: 'Researcher',
+            website: 'admin.local',
+        });
+    });
+
+    it('admin can edit other profile', function() {
+        cy.loginAs(adminUser);
+        cy.goToPath('/user/' + activeUser.user.uuid);
+
+        // Check new values
+        assertProfileValues({
+            firstName: 'Active',
+            lastName: 'User',
+            email: 'user@example.local',
+            username: 'user',
+            org: '',
+            org_email: '',
+            role: '',
+            website: '',
+        });
+
+        enterProfileValues({
+            org: 'Changed org name',
+            org_email: 'changed@example.com',
+            role: 'Researcher',
+            website: 'changed.local',
+        });
+        cy.get('[data-cy=profile-form] button[type="submit"]').click();
+
+        // Check new values
+        assertProfileValues({
+            firstName: 'Active',
+            lastName: 'User',
+            email: 'user@example.local',
+            username: 'user',
+            org: 'Changed org name',
+            org_email: 'changed@example.com',
+            role: 'Researcher',
+            website: 'changed.local',
+        });
+    });
+
+    it('displays role groups on user profile', function() {
+        cy.loginAs(adminUser);
+
+        cy.createGroup(adminUser.token, {
+            name: roleGroupName,
+            group_class: 'role',
+        }).as('roleGroup').then(function() {
+            cy.createLink(adminUser.token, {
+                name: 'can_write',
+                link_class: 'permission',
+                head_uuid: this.roleGroup.uuid,
+                tail_uuid: adminUser.user.uuid
+            });
+            cy.createLink(adminUser.token, {
+                name: 'can_write',
+                link_class: 'permission',
+                head_uuid: this.roleGroup.uuid,
+                tail_uuid: activeUser.user.uuid
+            });
+        });
+
+        cy.createGroup(adminUser.token, {
+            name: projectGroupName,
+            group_class: 'project',
+        }).as('projectGroup').then(function() {
+            cy.createLink(adminUser.token, {
+                name: 'can_write',
+                link_class: 'permission',
+                head_uuid: this.projectGroup.uuid,
+                tail_uuid: adminUser.user.uuid
+            });
+            cy.createLink(adminUser.token, {
+                name: 'can_write',
+                link_class: 'permission',
+                head_uuid: this.projectGroup.uuid,
+                tail_uuid: activeUser.user.uuid
+            });
+        });
+
+        cy.goToPath('/user/' + activeUser.user.uuid);
+        cy.get('div [role="tab"]').contains('GROUPS').click();
+        cy.get('[data-cy=user-profile-groups-data-explorer]').contains(roleGroupName);
+        cy.get('[data-cy=user-profile-groups-data-explorer]').should('not.contain', projectGroupName);
+
+        cy.goToPath('/user/' + adminUser.user.uuid);
+        cy.get('div [role="tab"]').contains('GROUPS').click();
+        cy.get('[data-cy=user-profile-groups-data-explorer]').contains(roleGroupName);
+        cy.get('[data-cy=user-profile-groups-data-explorer]').should('not.contain', projectGroupName);
+    });
+
+});
index d42fb6e197dcd271be3b0dd8f4cb5aaee534d007..82c20728209d8ef319f95172a566748afeb5e114 100644 (file)
@@ -84,6 +84,7 @@ export interface UserProfilePanelRootDataProps {
 }
 
 const RoleTypes = [
 }
 
 const RoleTypes = [
+    { key: '', value: '' },
     { key: 'Bio-informatician', value: 'Bio-informatician' },
     { key: 'Data Scientist', value: 'Data Scientist' },
     { key: 'Analyst', value: 'Analyst' },
     { key: 'Bio-informatician', value: 'Bio-informatician' },
     { key: 'Data Scientist', value: 'Data Scientist' },
     { key: 'Analyst', value: 'Analyst' },
@@ -150,11 +151,11 @@ export const userProfileGroupsColumns: DataColumns<string> = [
 
 const ReadOnlyField = withStyles(styles)(
     (props: ({ label: string, input: {value: string} }) & WithStyles<CssRules> ) => (
 
 const ReadOnlyField = withStyles(styles)(
     (props: ({ label: string, input: {value: string} }) & WithStyles<CssRules> ) => (
-        <Grid item xs={12}>
+        <Grid item xs={12} data-cy="field">
             <Typography className={props.classes.label}>
                 {props.label}
             </Typography>
             <Typography className={props.classes.label}>
                 {props.label}
             </Typography>
-            <Typography className={props.classes.readOnlyValue}>
+            <Typography className={props.classes.readOnlyValue} data-cy="value">
                 {props.input.value}
             </Typography>
         </Grid>
                 {props.input.value}
             </Typography>
         </Grid>
@@ -180,9 +181,9 @@ export const UserProfilePanelRoot = withStyles(styles)(
                 </Tabs>
                 {this.state.value === TABS.PROFILE &&
                     <CardContent>
                 </Tabs>
                 {this.state.value === TABS.PROFILE &&
                     <CardContent>
-                        <form onSubmit={this.props.handleSubmit}>
+                        <form onSubmit={this.props.handleSubmit} data-cy="profile-form">
                             <Grid container spacing={24}>
                             <Grid container spacing={24}>
-                                <Grid item className={this.props.classes.gridItem} sm={6} xs={12}>
+                                <Grid item className={this.props.classes.gridItem} sm={6} xs={12} data-cy="firstName">
                                     <Field
                                         label="First name"
                                         name="firstName"
                                     <Field
                                         label="First name"
                                         name="firstName"
@@ -190,7 +191,7 @@ export const UserProfilePanelRoot = withStyles(styles)(
                                         disabled
                                     />
                                 </Grid>
                                         disabled
                                     />
                                 </Grid>
-                                <Grid item className={this.props.classes.gridItem} sm={6} xs={12}>
+                                <Grid item className={this.props.classes.gridItem} sm={6} xs={12} data-cy="lastName">
                                     <Field
                                         label="Last name"
                                         name="lastName"
                                     <Field
                                         label="Last name"
                                         name="lastName"
@@ -198,7 +199,7 @@ export const UserProfilePanelRoot = withStyles(styles)(
                                         disabled
                                     />
                                 </Grid>
                                         disabled
                                     />
                                 </Grid>
-                                <Grid item className={this.props.classes.gridItem} sm={6} xs={12}>
+                                <Grid item className={this.props.classes.gridItem} sm={6} xs={12} data-cy="email">
                                     <Field
                                         label="E-mail"
                                         name="email"
                                     <Field
                                         label="E-mail"
                                         name="email"
@@ -206,7 +207,7 @@ export const UserProfilePanelRoot = withStyles(styles)(
                                         disabled
                                     />
                                 </Grid>
                                         disabled
                                     />
                                 </Grid>
-                                <Grid item className={this.props.classes.gridItem} sm={6} xs={12}>
+                                <Grid item className={this.props.classes.gridItem} sm={6} xs={12} data-cy="username">
                                     <Field
                                         label="Username"
                                         name="username"
                                     <Field
                                         label="Username"
                                         name="username"
@@ -219,7 +220,6 @@ export const UserProfilePanelRoot = withStyles(styles)(
                                         label="Organization"
                                         name="prefs.profile.organization"
                                         component={TextField as any}
                                         label="Organization"
                                         name="prefs.profile.organization"
                                         component={TextField as any}
-                                        validate={MY_ACCOUNT_VALIDATION}
                                         disabled={!this.props.isAdmin && !this.props.isSelf}
                                     />
                                 </Grid>
                                         disabled={!this.props.isAdmin && !this.props.isSelf}
                                     />
                                 </Grid>
@@ -228,7 +228,6 @@ export const UserProfilePanelRoot = withStyles(styles)(
                                         label="E-mail at Organization"
                                         name="prefs.profile.organization_email"
                                         component={TextField as any}
                                         label="E-mail at Organization"
                                         name="prefs.profile.organization_email"
                                         component={TextField as any}
-                                        validate={MY_ACCOUNT_VALIDATION}
                                         disabled={!this.props.isAdmin && !this.props.isSelf}
                                     />
                                 </Grid>
                                         disabled={!this.props.isAdmin && !this.props.isSelf}
                                     />
                                 </Grid>
@@ -270,8 +269,10 @@ export const UserProfilePanelRoot = withStyles(styles)(
                     <div className={this.props.classes.content}>
                         <DataExplorer
                                 id={USER_PROFILE_PANEL_ID}
                     <div className={this.props.classes.content}>
                         <DataExplorer
                                 id={USER_PROFILE_PANEL_ID}
+                                data-cy="user-profile-groups-data-explorer"
                                 onRowClick={noop}
                                 onRowDoubleClick={noop}
                                 onRowClick={noop}
                                 onRowDoubleClick={noop}
+                                onContextMenu={noop}
                                 contextMenuColumn={false}
                                 hideColumnSelector
                                 hideSearchInput
                                 contextMenuColumn={false}
                                 hideColumnSelector
                                 hideSearchInput