]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-16565 Preselect token type or project options if only one is available (#6297)
authorguillaume-peoch-sonarsource <91735163+guillaume-peoch-sonarsource@users.noreply.github.com>
Tue, 19 Jul 2022 12:04:38 +0000 (14:04 +0200)
committersonartech <sonartech@sonarsource.com>
Tue, 19 Jul 2022 20:03:21 +0000 (20:03 +0000)
server/sonar-web/src/main/js/apps/account/__tests__/Account-it.tsx
server/sonar-web/src/main/js/apps/users/components/TokensForm.tsx
server/sonar-web/src/main/js/apps/users/components/TokensFormModal.tsx
server/sonar-web/src/main/js/apps/users/components/__tests__/__snapshots__/TokensFormModal-test.tsx.snap

index 7abb80758a58923284e6dfbc7cfff876f1adf450..3fc8c1bbe54916e648a356d311162cf544966a63 100644 (file)
@@ -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);
index ec07f19b5e4ecc4ef378b59f67200c1e11e75ef3..8d0d345907ff9634852dcfa5d1bdcb9757a1ffd0 100644 (file)
@@ -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>
             )}
index 10f9f3ff748b95878a2511903cd02f4445072b21..1c98764458851f3fca5234fc79c6ec565c82a7d0 100644 (file)
@@ -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
index df708de673e79e75a8a55504ffb06e54198ac760..d52a6fcd16518aee3d7a180782c24fece637cca8 100644 (file)
@@ -4,7 +4,7 @@ exports[`should render correctly 1`] = `
 <Modal
   contentLabel="users.tokens"
   onRequestClose={[MockFunction]}
-  size="medium"
+  size="large"
 >
   <header
     className="modal-head"