Skip to content

Commit

Permalink
ui: Add organization and user account settings (PROJQUAY-4553)
Browse files Browse the repository at this point in the history
- This PR adds settings pages to the organization and user organization pages.
- Admin users can edit their preferences, billing, and organization type
- Updated cypress version to address bug cypress-io/cypress#25397
  • Loading branch information
jonathankingfc committed Oct 19, 2023
1 parent 38fd992 commit 2556a17
Show file tree
Hide file tree
Showing 18 changed files with 1,444 additions and 298 deletions.
2 changes: 1 addition & 1 deletion local-dev/stack/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ CORS_ORIGIN:
- "https://stage.foo.redhat.com:1337"
- "http://localhost:9000"
FEATURE_UI_V2: True
FEATURE_USER_METADATA: True
#RHSSO_LOGIN_CONFIG:
# CLIENT_ID: stage.quay.io
# CLIENT_SECRET: SECRET
Expand All @@ -85,4 +86,3 @@ FEATURE_UI_V2: True
# VERIFIED_EMAIL_CLAIM_NAME: email
# PREFERRED_USERNAME_CLAIM_NAME: preferred_username
# LOGIN_SCOPES: ['openid']

103 changes: 103 additions & 0 deletions web/cypress/e2e/account-settings.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/// <reference types="cypress" />

describe('Account Settings Page', () => {
beforeEach(() => {
cy.exec('npm run quay:seed');
cy.visit('/signin');
cy.request('GET', `${Cypress.env('REACT_QUAY_APP_API_URL')}/csrf_token`)
.then((response) => response.body.csrf_token)
.then((token) => {
cy.loginByCSRF(token);
});
cy.intercept('GET', '/config', {fixture: 'config.json'}).as('getConfig');
cy.intercept('GET', '/api/v1/plans/', {fixture: 'plans.json'}).as(
'getPlans',
);
});

it('General Settings', () => {
cy.visit('/organization/user1?tab=Settings');

// Type a bad e-mail
cy.get('#org-settings-email').clear();
cy.get('#org-settings-email').type('this is not a good e-mail');
cy.contains('Please enter a valid email address');

// Leave empty
cy.get('#org-settings-email').clear();
cy.contains('Please enter email associate with namespace');

// check is disabled
cy.get('#save-org-settings').should('be.disabled');
cy.get('#org-settings-email').clear();

// Type a good content
cy.get('#org-settings-email').type('good-email@redhat.com');
cy.get('#org-settings-fullname').type('Joe Smith');
cy.get('#org-settings-location').type('Raleigh, NC');
cy.get('#org-settings-company').type('Red Hat');
cy.get('#save-org-settings').click();

// refresh page and check if email is saved
cy.reload();
cy.get('#org-settings-email').should('have.value', 'good-email@redhat.com');
cy.get('#org-settings-fullname').should('have.value', 'Joe Smith');
cy.get('#org-settings-location').should('have.value', 'Raleigh, NC');
cy.get('#org-settings-company').should('have.value', 'Red Hat');
});

it('Billing Information', () => {
cy.visit('/organization/user1?tab=Settings');

// navigate to billing tab
cy.get('#pf-tab-1-billinginformation').click();

// Type a bad e-mail
cy.get('#billing-settings-invoice-email').clear();
cy.get('#billing-settings-invoice-email').type('this is not a good e-mail');

// check is disabled
cy.get('#save-billing-settings').should('be.disabled');
cy.get('#billing-settings-invoice-email').clear();

// Type a good e-mail and save
cy.get('#billing-settings-invoice-email').type('invoice-email@redhat.com');

// check save receipts
cy.get('#checkbox').should('not.be.checked');
cy.get('#checkbox').click();

// Save
cy.get('#save-billing-settings').click();

// refresh page, navigate to billing tab and check if email is saved
cy.reload();
cy.get('#pf-tab-1-billinginformation').click();
cy.get('#billing-settings-invoice-email').should(
'have.value',
'invoice-email@redhat.com',
);
cy.get('#checkbox').should('be.checked');
});

it('CLI Token', () => {
cy.visit('/organization/user1?tab=Settings');

// navigate to CLI Tab
cy.get('#pf-tab-2-cliconfig').click();

// Click generate password
cy.get('#cli-password-button').click();

// Wrong password
cy.get('#delete-confirmation-input').type('wrongpassword');
cy.get('#submit').click();
cy.contains('Invalid Username or Password');

// Correct password
cy.get('#delete-confirmation-input').clear();
cy.get('#delete-confirmation-input').type('password');
cy.get('#submit').click();
cy.contains('Your encrypted password is');
});
});
83 changes: 83 additions & 0 deletions web/cypress/e2e/org-settings.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/// <reference types="cypress" />

describe('Org Settings Page', () => {
beforeEach(() => {
cy.exec('npm run quay:seed');
cy.visit('/signin');
cy.request('GET', `${Cypress.env('REACT_QUAY_APP_API_URL')}/csrf_token`)
.then((response) => response.body.csrf_token)
.then((token) => {
cy.loginByCSRF(token);
});
cy.intercept('GET', '/config', {fixture: 'config.json'}).as('getConfig');
cy.intercept('GET', '/api/v1/plans/', {fixture: 'plans.json'}).as(
'getPlans',
);
});

it('General Settings', () => {
cy.visit('/organization/projectquay?tab=Settings');

// Type a bad e-mail
cy.get('#org-settings-email').clear();
cy.get('#org-settings-email').type('this is not a good e-mail');
cy.contains('Please enter a valid email address');

// Leave empty
cy.get('#org-settings-email').clear();
cy.contains('Please enter email associate with namespace');

// check is disabled
cy.get('#save-org-settings').should('be.disabled');
cy.get('#org-settings-email').clear();

// Type a good e-mail and save
cy.get('#org-settings-email').type('good-email@redhat.com');
cy.get('#save-org-settings').click();

// refresh page and check if email is saved
cy.reload();
cy.get('#org-settings-email').should('have.value', 'good-email@redhat.com');
});

it('Billing Information', () => {
cy.visit('/organization/projectquay?tab=Settings');

// navigate to billing tab
cy.get('#pf-tab-1-billinginformation').click();

// Type a bad e-mail
cy.get('#billing-settings-invoice-email').clear();
cy.get('#billing-settings-invoice-email').type('this is not a good e-mail');

// check is disabled
cy.get('#save-billing-settings').should('be.disabled');
cy.get('#billing-settings-invoice-email').clear();

// Type a good e-mail and save
cy.get('#billing-settings-invoice-email').type('invoice-email@redhat.com');

// check save receipts
cy.get('#checkbox').should('not.be.checked');
cy.get('#checkbox').click();

// Save
cy.get('#save-billing-settings').click();

// refresh page, navigate to billing tab and check if email is saved
cy.reload();
cy.get('#pf-tab-1-billinginformation').click();
cy.get('#billing-settings-invoice-email').should(
'have.value',
'invoice-email@redhat.com',
);
cy.get('#checkbox').should('be.checked');
});

it('Cli Token Invisible', () => {
cy.visit('/organization/projectquay?tab=Settings');

// ensure cli token tab is not on page
cy.get('#pf-tab-2-cliconfig').should('not.exist');
});
});
4 changes: 2 additions & 2 deletions web/cypress/fixtures/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@
"ANONYMOUS_ACCESS": true,
"APP_REGISTRY": false,
"APP_SPECIFIC_TOKENS": true,
"BILLING": false,
"BILLING": true,
"BITBUCKET_BUILD": false,
"BLACKLISTED_EMAILS": false,
"BUILD_SUPPORT": true,
Expand Down Expand Up @@ -126,7 +126,7 @@
"USER_INITIALIZE": false,
"USER_LAST_ACCESSED": true,
"USER_LOG_ACCESS": false,
"USER_METADATA": false,
"USER_METADATA": true,
"USER_RENAME": false
},
"oauth": {
Expand Down
82 changes: 82 additions & 0 deletions web/src/components/modals/GenerateEncryptedPasswordModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import {Modal, ModalVariant, Button, TextInput} from '@patternfly/react-core';
import {useState} from 'react';
import FormError from 'src/components/errors/FormError';
import {addDisplayError} from 'src/resources/ErrorHandling';
import {useCreateClientKey} from 'src/hooks/UseCreateClientKey';

export function GenerateEncryptedPassword(props: ConfirmationModalProps) {
const [err, setErr] = useState<string>();

const [password, setPassword] = useState('');
const [step, setStep] = useState(1);
const {createClientKey, clientKey} = useCreateClientKey({
onError: (error) => {
console.error(error);
setErr(addDisplayError('Error', error));
},
onSuccess: () => {
setStep(step + 1);
},
});

const handleModalConfirm = async () => {
createClientKey(password);
};

return (
<Modal
variant={ModalVariant.small}
title={props.title}
isOpen={props.modalOpen}
onClose={props.toggleModal}
actions={
step == 1
? [
<Button
key="confirm"
variant="primary"
onClick={handleModalConfirm}
id="submit"
>
{props.buttonText}
</Button>,
<Button key="cancel" variant="link" onClick={props.toggleModal}>
Cancel
</Button>,
]
: [
<Button key="cancel" variant="link" onClick={props.toggleModal}>
Done
</Button>,
]
}
>
{step == 1 && (
<>
<FormError message={err} setErr={setErr} />
<TextInput
id="delete-confirmation-input"
value={password}
type="password"
onChange={(_, value) => setPassword(value)}
aria-label="text input example"
label="Password"
/>
Please enter your password in order to generate
</>
)}
{step == 2 && (
<>
Your encrypted password is: <br /> {clientKey}
</>
)}
</Modal>
);
}

type ConfirmationModalProps = {
title: string;
modalOpen: boolean;
buttonText: string;
toggleModal: () => void;
};

0 comments on commit 2556a17

Please sign in to comment.