diff options
author | guillaume-peoch-sonarsource <91735163+guillaume-peoch-sonarsource@users.noreply.github.com> | 2022-07-19 14:04:38 +0200 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2022-07-19 20:03:21 +0000 |
commit | 19e81c42496a55c85343d4692bb2daf559cb7882 (patch) | |
tree | 8a982965ac26eb743cbb2241a6c5d4b55ccaeb77 | |
parent | 335dee8062f90f425daa5b41e3e2d6e1f98ffba9 (diff) | |
download | sonarqube-19e81c42496a55c85343d4692bb2daf559cb7882.tar.gz sonarqube-19e81c42496a55c85343d4692bb2daf559cb7882.zip |
SONAR-16565 Preselect token type or project options if only one is available (#6297)
4 files changed, 55 insertions, 17 deletions
diff --git a/server/sonar-web/src/main/js/apps/account/__tests__/Account-it.tsx b/server/sonar-web/src/main/js/apps/account/__tests__/Account-it.tsx index 7abb80758a5..3fc8c1bbe54 100644 --- a/server/sonar-web/src/main/js/apps/account/__tests__/Account-it.tsx +++ b/server/sonar-web/src/main/js/apps/account/__tests__/Account-it.tsx @@ -344,7 +344,7 @@ describe('security page', () => { ).not.toBeInTheDocument(); }); - it("should not suggest creating a Project token if the user doesn't have at least one scannable Projects", async () => { + it("should not suggest creating a Project token if the user doesn't have at least one scannable Projects", () => { (getScannableProjects as jest.Mock).mockResolvedValueOnce({ projects: [] }); @@ -353,10 +353,41 @@ describe('security page', () => { securityPagePath ); - await selectEvent.openMenu(screen.getAllByRole('textbox')[1]); + selectEvent.openMenu(screen.getAllByRole('textbox')[1]); expect(screen.queryByText(`users.tokens.${TokenType.Project}`)).not.toBeInTheDocument(); }); + it('should preselect the user token type if the user has no scan rights', async () => { + (getScannableProjects as jest.Mock).mockResolvedValueOnce({ + projects: [] + }); + renderAccountApp(mockLoggedInUser(), securityPagePath); + + const globalToken = await screen.findByText(`users.tokens.${TokenType.User}`); + expect(globalToken).toBeInTheDocument(); + }); + + it('should preselect the only project the user has access to if they select project token', async () => { + (getScannableProjects as jest.Mock).mockResolvedValueOnce({ + projects: [ + { + key: 'project-key-1', + name: 'Project Name 1' + } + ] + }); + renderAccountApp( + mockLoggedInUser({ permissions: { global: [Permissions.Scan] } }), + securityPagePath + ); + + await selectEvent.select(screen.getAllByRole('textbox')[1], [ + `users.tokens.${TokenType.Project}` + ]); + + expect(screen.getByText('Project Name 1')).toBeInTheDocument(); + }); + it('should allow local users to change password', async () => { const user = userEvent.setup(); renderAccountApp(mockLoggedInUser({ local: true }), securityPagePath); diff --git a/server/sonar-web/src/main/js/apps/users/components/TokensForm.tsx b/server/sonar-web/src/main/js/apps/users/components/TokensForm.tsx index ec07f19b5e4..8d0d345907f 100644 --- a/server/sonar-web/src/main/js/apps/users/components/TokensForm.tsx +++ b/server/sonar-web/src/main/js/apps/users/components/TokensForm.tsx @@ -54,7 +54,7 @@ interface State { newTokenType?: TokenType; tokens: UserToken[]; projects: BasicSelectOption[]; - selectedProject: { key: string; name: string }; + selectedProject?: BasicSelectOption; newTokenExpiration: TokenExpiration; tokenExpirationOptions: { value: TokenExpiration; label: string }[]; } @@ -66,7 +66,6 @@ export class TokensForm extends React.PureComponent<Props, State> { loading: true, newTokenName: '', newTokenType: this.props.displayTokenTypeInput ? undefined : TokenType.User, - selectedProject: { key: '', name: '' }, tokens: [], projects: [], newTokenExpiration: TokenExpiration.OneMonth, @@ -113,7 +112,8 @@ export class TokensForm extends React.PureComponent<Props, State> { const { projects: projectArray } = await getScannableProjects(); const projects = projectArray.map(project => ({ label: project.name, value: project.key })); this.setState({ - projects + projects, + selectedProject: projects.length === 1 ? projects[0] : undefined }); }; @@ -139,7 +139,8 @@ export class TokensForm extends React.PureComponent<Props, State> { name: newTokenName, login, type: newTokenType, - ...(newTokenType === TokenType.Project && { projectKey: selectedProject.key }), + ...(newTokenType === TokenType.Project && + selectedProject !== undefined && { projectKey: selectedProject.value }), ...(newTokenExpiration !== TokenExpiration.NoExpiration && { expirationDate: computeTokenExpirationDate(newTokenExpiration) }) @@ -155,17 +156,19 @@ export class TokensForm extends React.PureComponent<Props, State> { isExpired: false, expirationDate: newToken.expirationDate, type: newTokenType, - ...(newTokenType === TokenType.Project && { - project: { key: selectedProject.key, name: selectedProject.name } - }) + ...(newTokenType === TokenType.Project && + selectedProject !== undefined && { + project: { key: selectedProject.value, name: selectedProject.label } + }) } ]; return { generating: false, newToken, newTokenName: '', - selectedProject: { key: '', name: '' }, + selectedProject: undefined, newTokenType: undefined, + newTokenExpiration: TokenExpiration.OneMonth, tokens }; }, this.updateTokensCount); @@ -198,7 +201,7 @@ export class TokensForm extends React.PureComponent<Props, State> { return true; } if (newTokenType === TokenType.Project) { - return !selectedProject.key; + return !selectedProject?.value; } return !newTokenType; @@ -212,8 +215,8 @@ export class TokensForm extends React.PureComponent<Props, State> { this.setState({ newTokenType: value }); }; - handleProjectChange = ({ value, label }: { value: string; label: string }) => { - this.setState({ selectedProject: { key: value, name: label } }); + handleProjectChange = (selectedProject: BasicSelectOption) => { + this.setState({ selectedProject }); }; handleNewTokenExpirationChange = ({ value }: { value: TokenExpiration }) => { @@ -277,7 +280,11 @@ export class TokensForm extends React.PureComponent<Props, State> { onChange={this.handleNewTokenTypeChange} options={tokenTypeOptions} placeholder={translate('users.tokens.select_type')} - value={tokenTypeOptions.find(option => option.value === newTokenType) || null} + value={ + tokenTypeOptions.length === 1 + ? tokenTypeOptions[0] + : tokenTypeOptions.find(option => option.value === newTokenType) || null + } /> </div> {newTokenType === TokenType.Project && ( @@ -291,7 +298,7 @@ export class TokensForm extends React.PureComponent<Props, State> { onChange={this.handleProjectChange} options={projects} placeholder={translate('users.tokens.select_project')} - value={projects.find(project => project.value === selectedProject.key)} + value={selectedProject} /> </div> )} diff --git a/server/sonar-web/src/main/js/apps/users/components/TokensFormModal.tsx b/server/sonar-web/src/main/js/apps/users/components/TokensFormModal.tsx index 10f9f3ff748..1c987644588 100644 --- a/server/sonar-web/src/main/js/apps/users/components/TokensFormModal.tsx +++ b/server/sonar-web/src/main/js/apps/users/components/TokensFormModal.tsx @@ -33,7 +33,7 @@ interface Props { export default function TokensFormModal(props: Props) { return ( - <Modal size="medium" contentLabel={translate('users.tokens')} onRequestClose={props.onClose}> + <Modal size="large" contentLabel={translate('users.tokens')} onRequestClose={props.onClose}> <header className="modal-head"> <h2> <FormattedMessage diff --git a/server/sonar-web/src/main/js/apps/users/components/__tests__/__snapshots__/TokensFormModal-test.tsx.snap b/server/sonar-web/src/main/js/apps/users/components/__tests__/__snapshots__/TokensFormModal-test.tsx.snap index df708de673e..d52a6fcd165 100644 --- a/server/sonar-web/src/main/js/apps/users/components/__tests__/__snapshots__/TokensFormModal-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/users/components/__tests__/__snapshots__/TokensFormModal-test.tsx.snap @@ -4,7 +4,7 @@ exports[`should render correctly 1`] = ` <Modal contentLabel="users.tokens" onRequestClose={[MockFunction]} - size="medium" + size="large" > <header className="modal-head" |