]> source.dussan.org Git - sonarqube.git/commitdiff
Hardening 28-03 (#72)
authorPascal Mugnier <pascal.mugnier@sonarsource.com>
Wed, 4 Apr 2018 07:55:31 +0000 (09:55 +0200)
committerSonarTech <sonartech@sonarsource.com>
Wed, 4 Apr 2018 13:18:42 +0000 (15:18 +0200)
server/sonar-web/public/index.html
server/sonar-web/src/main/js/api/components.ts
server/sonar-web/src/main/js/app/components/nav/component/ComponentNavBranchesMenuItem.tsx
server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavBranchesMenuItem-test.tsx.snap
server/sonar-web/src/main/js/apps/projectsManagement/CreateProjectForm.tsx
server/sonar-web/src/main/js/apps/projectsManagement/__tests__/CreateProjectForm-test.tsx
server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/CreateProjectForm-test.tsx.snap
server/sonar-web/src/main/js/components/common/BranchStatus.tsx
sonar-core/src/main/resources/org/sonar/l10n/core.properties

index c7feadbcc692880f292de75bd9bdfa6d37e2172a..71b679d96f480fd8dc25d479a4fa4ebe63d5f7cb 100644 (file)
@@ -1,42 +1,45 @@
 <!DOCTYPE html>
 <html lang="en">
-  <head>
-    <meta http-equiv="content-type" content="text/html; charset=UTF-8" charset="UTF-8"/>
-    <meta http-equiv="X-UA-Compatible" content="IE=edge">
 
-    <link rel="apple-touch-icon" href="%WEB_CONTEXT%/apple-touch-icon.png">
-    <link rel="apple-touch-icon" sizes="57x57" href="%WEB_CONTEXT%/apple-touch-icon-57x57.png">
-    <link rel="apple-touch-icon" sizes="60x60" href="%WEB_CONTEXT%/apple-touch-icon-60x60.png">
-    <link rel="apple-touch-icon" sizes="72x72" href="%WEB_CONTEXT%/apple-touch-icon-72x72.png">
-    <link rel="apple-touch-icon" sizes="76x76" href="%WEB_CONTEXT%/apple-touch-icon-76x76.png">
-    <link rel="apple-touch-icon" sizes="114x114" href="%WEB_CONTEXT%/apple-touch-icon-114x114.png">
-    <link rel="apple-touch-icon" sizes="120x120" href="%WEB_CONTEXT%/apple-touch-icon-120x120.png">
-    <link rel="apple-touch-icon" sizes="144x144" href="%WEB_CONTEXT%/apple-touch-icon-144x144.png">
-    <link rel="apple-touch-icon" sizes="152x152" href="%WEB_CONTEXT%/apple-touch-icon-152x152.png">
-    <link rel="apple-touch-icon" sizes="180x180" href="%WEB_CONTEXT%/apple-touch-icon-180x180.png">
+<head>
+  <meta http-equiv="content-type" content="text/html; charset=UTF-8" charset="UTF-8" />
+  <meta http-equiv="X-UA-Compatible" content="IE=edge">
 
-    <link rel="icon" type="image/x-icon" href="%WEB_CONTEXT%/favicon.ico">
+  <link rel="apple-touch-icon" href="%WEB_CONTEXT%/apple-touch-icon.png">
+  <link rel="apple-touch-icon" sizes="57x57" href="%WEB_CONTEXT%/apple-touch-icon-57x57.png">
+  <link rel="apple-touch-icon" sizes="60x60" href="%WEB_CONTEXT%/apple-touch-icon-60x60.png">
+  <link rel="apple-touch-icon" sizes="72x72" href="%WEB_CONTEXT%/apple-touch-icon-72x72.png">
+  <link rel="apple-touch-icon" sizes="76x76" href="%WEB_CONTEXT%/apple-touch-icon-76x76.png">
+  <link rel="apple-touch-icon" sizes="114x114" href="%WEB_CONTEXT%/apple-touch-icon-114x114.png">
+  <link rel="apple-touch-icon" sizes="120x120" href="%WEB_CONTEXT%/apple-touch-icon-120x120.png">
+  <link rel="apple-touch-icon" sizes="144x144" href="%WEB_CONTEXT%/apple-touch-icon-144x144.png">
+  <link rel="apple-touch-icon" sizes="152x152" href="%WEB_CONTEXT%/apple-touch-icon-152x152.png">
+  <link rel="apple-touch-icon" sizes="180x180" href="%WEB_CONTEXT%/apple-touch-icon-180x180.png">
 
-    <meta name="application-name" content="SonarQube"/>
+  <link rel="icon" type="image/x-icon" href="%WEB_CONTEXT%/favicon.ico">
 
-    <meta name="msapplication-TileColor" content="#FFFFFF" />
-    <meta name="msapplication-TileImage" content="%WEB_CONTEXT%/mstile-512x512.png" />
+  <meta name="application-name" content="SonarQube" />
 
-    <% for (var css in htmlWebpackPlugin.files.css) { %>
+  <meta name="msapplication-TileColor" content="#FFFFFF" />
+  <meta name="msapplication-TileImage" content="%WEB_CONTEXT%/mstile-512x512.png" />
+
+  <% for (var css in htmlWebpackPlugin.files.css) { %>
     <link href="%WEB_CONTEXT%/<%= htmlWebpackPlugin.files.css[css] %>" rel="stylesheet">
     <% } %>
-    <title>SonarQube</title>
-  </head>
-  <body>
-    <div id="content">
-      <div class="global-loading">
-        <i class="spinner global-loading-spinner"></i>
-        <span class="global-loading-text">Loading...</span>
-      </div>
+      <title>Loading...</title>
+</head>
+
+<body>
+  <div id="content">
+    <div class="global-loading">
+      <i class="spinner global-loading-spinner"></i>
+      <span class="global-loading-text">Loading...</span>
     </div>
-    <script>window.baseUrl = '%WEB_CONTEXT%';</script>
-    <% for (var chunk in htmlWebpackPlugin.files.chunks) { %>
+  </div>
+  <script>window.baseUrl = '%WEB_CONTEXT%';</script>
+  <% for (var chunk in htmlWebpackPlugin.files.chunks) { %>
     <script src="%WEB_CONTEXT%/<%= htmlWebpackPlugin.files.chunks[chunk].entry %>"></script>
     <% } %>
-  </body>
+</body>
+
 </html>
index 60f7af8b14b6e5e3ba6ae9cf48b4d82accfe8519..4505e8e928243555047f58250d7bbcadaa535dc3 100644 (file)
@@ -70,7 +70,6 @@ export function deletePortfolio(portfolio: string): Promise<void | Response> {
 }
 
 export function createProject(data: {
-  branch?: string;
   name: string;
   project: string;
   organization?: string;
index 9375bbf356c78d51780c26604b5489454c72aba5..316ee5f06b1a126db078d1a378b8b5c3a20fb18b 100644 (file)
@@ -52,12 +52,12 @@ export default function ComponentNavBranchesMenuItem({ branchLike, ...props }: P
 
   return (
     <li key={getBranchLikeKey(branchLike)} onMouseEnter={handleMouseEnter}>
-      <Tooltip mouseEnterDelay={0.5} overlay={displayName} placement="right">
-        <Link
-          className={classNames('navbar-context-meta-branch-menu-item', {
-            active: props.selected
-          })}
-          to={getBranchLikeUrl(props.component.key, branchLike)}>
+      <Link
+        className={classNames('navbar-context-meta-branch-menu-item', {
+          active: props.selected
+        })}
+        to={getBranchLikeUrl(props.component.key, branchLike)}>
+        <Tooltip mouseEnterDelay={0.5} overlay={displayName} placement="left">
           <div className="navbar-context-meta-branch-menu-item-name text-ellipsis">
             <BranchIcon
               branchLike={branchLike}
@@ -68,11 +68,11 @@ export default function ComponentNavBranchesMenuItem({ branchLike, ...props }: P
               <div className="outline-badge spacer-left">{translate('branches.main_branch')}</div>
             )}
           </div>
-          <div className="big-spacer-left note">
-            <BranchStatus branchLike={branchLike} concise={true} />
-          </div>
-        </Link>
-      </Tooltip>
+        </Tooltip>
+        <div className="big-spacer-left note">
+          <BranchStatus branchLike={branchLike} concise={true} />
+        </div>
+      </Link>
     </li>
   );
 }
index df7c8da7b472b5608da7373063738d089d8a15c1..463a43a173f59e754476b42729c2a56d856cad61 100644 (file)
@@ -5,23 +5,23 @@ exports[`renders main branch 1`] = `
   key="branch-master"
   onMouseEnter={[Function]}
 >
-  <Tooltip
-    mouseEnterDelay={0.5}
-    overlay="master"
-    placement="right"
-  >
-    <Link
-      className="navbar-context-meta-branch-menu-item"
-      onlyActiveOnIndex={false}
-      style={Object {}}
-      to={
-        Object {
-          "pathname": "/dashboard",
-          "query": Object {
-            "id": "component",
-          },
-        }
+  <Link
+    className="navbar-context-meta-branch-menu-item"
+    onlyActiveOnIndex={false}
+    style={Object {}}
+    to={
+      Object {
+        "pathname": "/dashboard",
+        "query": Object {
+          "id": "component",
+        },
       }
+    }
+  >
+    <Tooltip
+      mouseEnterDelay={0.5}
+      overlay="master"
+      placement="left"
     >
       <div
         className="navbar-context-meta-branch-menu-item-name text-ellipsis"
@@ -42,21 +42,21 @@ exports[`renders main branch 1`] = `
           branches.main_branch
         </div>
       </div>
-      <div
-        className="big-spacer-left note"
-      >
-        <BranchStatus
-          branchLike={
-            Object {
-              "isMain": true,
-              "name": "master",
-            }
+    </Tooltip>
+    <div
+      className="big-spacer-left note"
+    >
+      <BranchStatus
+        branchLike={
+          Object {
+            "isMain": true,
+            "name": "master",
           }
-          concise={true}
-        />
-      </div>
-    </Link>
-  </Tooltip>
+        }
+        concise={true}
+      />
+    </div>
+  </Link>
 </li>
 `;
 
@@ -65,25 +65,25 @@ exports[`renders short-living branch 1`] = `
   key="branch-foo"
   onMouseEnter={[Function]}
 >
-  <Tooltip
-    mouseEnterDelay={0.5}
-    overlay="foo"
-    placement="right"
-  >
-    <Link
-      className="navbar-context-meta-branch-menu-item"
-      onlyActiveOnIndex={false}
-      style={Object {}}
-      to={
-        Object {
-          "pathname": "/project/issues",
-          "query": Object {
-            "branch": "foo",
-            "id": "component",
-            "resolved": "false",
-          },
-        }
+  <Link
+    className="navbar-context-meta-branch-menu-item"
+    onlyActiveOnIndex={false}
+    style={Object {}}
+    to={
+      Object {
+        "pathname": "/project/issues",
+        "query": Object {
+          "branch": "foo",
+          "id": "component",
+          "resolved": "false",
+        },
       }
+    }
+  >
+    <Tooltip
+      mouseEnterDelay={0.5}
+      overlay="foo"
+      placement="left"
     >
       <div
         className="navbar-context-meta-branch-menu-item-name text-ellipsis"
@@ -107,29 +107,29 @@ exports[`renders short-living branch 1`] = `
         />
         foo
       </div>
-      <div
-        className="big-spacer-left note"
-      >
-        <BranchStatus
-          branchLike={
-            Object {
-              "isMain": false,
-              "mergeBranch": "master",
-              "name": "foo",
-              "status": Object {
-                "bugs": 1,
-                "codeSmells": 2,
-                "qualityGateStatus": "ERROR",
-                "vulnerabilities": 3,
-              },
-              "type": "SHORT",
-            }
+    </Tooltip>
+    <div
+      className="big-spacer-left note"
+    >
+      <BranchStatus
+        branchLike={
+          Object {
+            "isMain": false,
+            "mergeBranch": "master",
+            "name": "foo",
+            "status": Object {
+              "bugs": 1,
+              "codeSmells": 2,
+              "qualityGateStatus": "ERROR",
+              "vulnerabilities": 3,
+            },
+            "type": "SHORT",
           }
-          concise={true}
-        />
-      </div>
-    </Link>
-  </Tooltip>
+        }
+        concise={true}
+      />
+    </div>
+  </Link>
 </li>
 `;
 
@@ -138,25 +138,25 @@ exports[`renders short-living orhpan branch 1`] = `
   key="branch-foo"
   onMouseEnter={[Function]}
 >
-  <Tooltip
-    mouseEnterDelay={0.5}
-    overlay="foo"
-    placement="right"
-  >
-    <Link
-      className="navbar-context-meta-branch-menu-item"
-      onlyActiveOnIndex={false}
-      style={Object {}}
-      to={
-        Object {
-          "pathname": "/project/issues",
-          "query": Object {
-            "branch": "foo",
-            "id": "component",
-            "resolved": "false",
-          },
-        }
+  <Link
+    className="navbar-context-meta-branch-menu-item"
+    onlyActiveOnIndex={false}
+    style={Object {}}
+    to={
+      Object {
+        "pathname": "/project/issues",
+        "query": Object {
+          "branch": "foo",
+          "id": "component",
+          "resolved": "false",
+        },
       }
+    }
+  >
+    <Tooltip
+      mouseEnterDelay={0.5}
+      overlay="foo"
+      placement="left"
     >
       <div
         className="navbar-context-meta-branch-menu-item-name text-ellipsis"
@@ -181,29 +181,29 @@ exports[`renders short-living orhpan branch 1`] = `
         />
         foo
       </div>
-      <div
-        className="big-spacer-left note"
-      >
-        <BranchStatus
-          branchLike={
-            Object {
-              "isMain": false,
-              "isOrphan": true,
-              "mergeBranch": "master",
-              "name": "foo",
-              "status": Object {
-                "bugs": 1,
-                "codeSmells": 2,
-                "qualityGateStatus": "ERROR",
-                "vulnerabilities": 3,
-              },
-              "type": "SHORT",
-            }
+    </Tooltip>
+    <div
+      className="big-spacer-left note"
+    >
+      <BranchStatus
+        branchLike={
+          Object {
+            "isMain": false,
+            "isOrphan": true,
+            "mergeBranch": "master",
+            "name": "foo",
+            "status": Object {
+              "bugs": 1,
+              "codeSmells": 2,
+              "qualityGateStatus": "ERROR",
+              "vulnerabilities": 3,
+            },
+            "type": "SHORT",
           }
-          concise={true}
-        />
-      </div>
-    </Link>
-  </Tooltip>
+        }
+        concise={true}
+      />
+    </div>
+  </Link>
 </li>
 `;
index 5a39b28c5f8857922fcc63d0b1b4a7d3b5f4b26c..b3e90fd06b2dbb363bfc3f65b31929d832f48320 100644 (file)
@@ -36,8 +36,6 @@ interface Props {
 }
 
 interface State {
-  advanced: boolean;
-  branch: string;
   createdProject?: { key: string; name: string };
   key: string;
   loading: boolean;
@@ -54,8 +52,6 @@ export default class CreateProjectForm extends React.PureComponent<Props, State>
   constructor(props: Props) {
     super(props);
     this.state = {
-      advanced: false,
-      branch: '',
       key: '',
       loading: false,
       name: '',
@@ -80,12 +76,6 @@ export default class CreateProjectForm extends React.PureComponent<Props, State>
     this.mounted = false;
   }
 
-  handleAdvancedClick = (event: React.SyntheticEvent<HTMLAnchorElement>) => {
-    event.preventDefault();
-    event.currentTarget.blur();
-    this.setState(state => ({ advanced: !state.advanced }));
-  };
-
   handleInputChange = (event: React.SyntheticEvent<HTMLInputElement>) => {
     const { name, value } = event.currentTarget;
     this.setState({ [name]: value });
@@ -100,7 +90,6 @@ export default class CreateProjectForm extends React.PureComponent<Props, State>
 
     const data = {
       name: this.state.name,
-      branch: this.state.branch,
       organization: this.props.organization && this.props.organization.key,
       project: this.state.key,
       visibility: this.state.visibility
@@ -211,29 +200,6 @@ export default class CreateProjectForm extends React.PureComponent<Props, State>
                   </div>
                 )}
               </div>
-              {this.state.advanced ? (
-                <div className="modal-field big-spacer-top">
-                  <label htmlFor="create-project-branch">{translate('branch')}</label>
-                  <input
-                    autoFocus={true}
-                    id="create-project-branch"
-                    maxLength={200}
-                    name="branch"
-                    onChange={this.handleInputChange}
-                    type="text"
-                    value={this.state.branch}
-                  />
-                </div>
-              ) : (
-                <div className="modal-field big-spacer-top">
-                  <a
-                    className="js-more text-muted note"
-                    href="#"
-                    onClick={this.handleAdvancedClick}>
-                    {translate('more')}
-                  </a>
-                </div>
-              )}
             </div>
 
             <footer className="modal-foot">
index d79aa247497ca9177811c3e85d51ba776fedea6b..96ea19b42a262ec0bffaf7e57cab21db9132d417 100644 (file)
@@ -27,7 +27,7 @@ jest.mock('../../../api/components', () => ({
 import * as React from 'react';
 import { shallow } from 'enzyme';
 import CreateProjectForm from '../CreateProjectForm';
-import { change, submit, click, waitAndUpdate } from '../../../helpers/testUtils';
+import { change, submit, waitAndUpdate } from '../../../helpers/testUtils';
 import { Visibility } from '../../../app/types';
 
 const createProject = require('../../../api/components').createProject as jest.Mock<any>;
@@ -57,7 +57,6 @@ it('creates project', async () => {
 
   submit(wrapper.find('form'));
   expect(createProject).toBeCalledWith({
-    branch: '',
     name: 'name',
     organization: 'org',
     project: 'key',
@@ -68,17 +67,3 @@ it('creates project', async () => {
   await waitAndUpdate(wrapper);
   expect(wrapper).toMatchSnapshot();
 });
-
-it('shows more', () => {
-  const wrapper = shallow(
-    <CreateProjectForm
-      onClose={jest.fn()}
-      onProjectCreated={jest.fn()}
-      organization={organization}
-    />
-  );
-  expect(wrapper).toMatchSnapshot();
-
-  click(wrapper.find('.js-more'));
-  expect(wrapper).toMatchSnapshot();
-});
index b0b662adaaedfb57072b7a80fb86fcb9a42135f6..98ca96135f8c882d71d4fddad80a84d2885c8bba 100644 (file)
@@ -85,17 +85,6 @@ exports[`creates project 1`] = `
           />
         </div>
       </div>
-      <div
-        className="modal-field big-spacer-top"
-      >
-        <a
-          className="js-more text-muted note"
-          href="#"
-          onClick={[Function]}
-        >
-          more
-        </a>
-      </div>
     </div>
     <footer
       className="modal-foot"
@@ -202,17 +191,6 @@ exports[`creates project 2`] = `
           />
         </div>
       </div>
-      <div
-        className="modal-field big-spacer-top"
-      >
-        <a
-          className="js-more text-muted note"
-          href="#"
-          onClick={[Function]}
-        >
-          more
-        </a>
-      </div>
     </div>
     <footer
       className="modal-foot"
@@ -319,17 +297,6 @@ exports[`creates project 3`] = `
           />
         </div>
       </div>
-      <div
-        className="modal-field big-spacer-top"
-      >
-        <a
-          className="js-more text-muted note"
-          href="#"
-          onClick={[Function]}
-        >
-          more
-        </a>
-      </div>
     </div>
     <footer
       className="modal-foot"
@@ -411,244 +378,3 @@ exports[`creates project 4`] = `
   </div>
 </Modal>
 `;
-
-exports[`shows more 1`] = `
-<Modal
-  contentLabel="modal form"
-  onRequestClose={[MockFunction]}
->
-  <form
-    id="create-project-form"
-    onSubmit={[Function]}
-  >
-    <header
-      className="modal-head"
-    >
-      <h2>
-        qualifiers.create.TRK
-      </h2>
-    </header>
-    <div
-      className="modal-body"
-    >
-      <div
-        className="modal-field"
-      >
-        <label
-          htmlFor="create-project-name"
-        >
-          name
-          <em
-            className="mandatory"
-          >
-            *
-          </em>
-        </label>
-        <input
-          autoFocus={true}
-          id="create-project-name"
-          maxLength={2000}
-          name="name"
-          onChange={[Function]}
-          required={true}
-          type="text"
-          value=""
-        />
-      </div>
-      <div
-        className="modal-field"
-      >
-        <label
-          htmlFor="create-project-key"
-        >
-          key
-          <em
-            className="mandatory"
-          >
-            *
-          </em>
-        </label>
-        <input
-          id="create-project-key"
-          maxLength={400}
-          name="key"
-          onChange={[Function]}
-          required={true}
-          type="text"
-          value=""
-        />
-      </div>
-      <div
-        className="modal-field"
-      >
-        <label>
-          visibility
-        </label>
-        <VisibilitySelector
-          className="little-spacer-top"
-          onChange={[Function]}
-          visibility="public"
-        />
-        <div
-          className="spacer-top"
-        >
-          <UpgradeOrganizationBox
-            organization="org"
-          />
-        </div>
-      </div>
-      <div
-        className="modal-field big-spacer-top"
-      >
-        <a
-          className="js-more text-muted note"
-          href="#"
-          onClick={[Function]}
-        >
-          more
-        </a>
-      </div>
-    </div>
-    <footer
-      className="modal-foot"
-    >
-      <SubmitButton
-        disabled={false}
-        id="create-project-submit"
-      >
-        create
-      </SubmitButton>
-      <ResetButtonLink
-        id="create-project-cancel"
-        onClick={[MockFunction]}
-      >
-        cancel
-      </ResetButtonLink>
-    </footer>
-  </form>
-</Modal>
-`;
-
-exports[`shows more 2`] = `
-<Modal
-  contentLabel="modal form"
-  onRequestClose={[MockFunction]}
->
-  <form
-    id="create-project-form"
-    onSubmit={[Function]}
-  >
-    <header
-      className="modal-head"
-    >
-      <h2>
-        qualifiers.create.TRK
-      </h2>
-    </header>
-    <div
-      className="modal-body"
-    >
-      <div
-        className="modal-field"
-      >
-        <label
-          htmlFor="create-project-name"
-        >
-          name
-          <em
-            className="mandatory"
-          >
-            *
-          </em>
-        </label>
-        <input
-          autoFocus={true}
-          id="create-project-name"
-          maxLength={2000}
-          name="name"
-          onChange={[Function]}
-          required={true}
-          type="text"
-          value=""
-        />
-      </div>
-      <div
-        className="modal-field"
-      >
-        <label
-          htmlFor="create-project-key"
-        >
-          key
-          <em
-            className="mandatory"
-          >
-            *
-          </em>
-        </label>
-        <input
-          id="create-project-key"
-          maxLength={400}
-          name="key"
-          onChange={[Function]}
-          required={true}
-          type="text"
-          value=""
-        />
-      </div>
-      <div
-        className="modal-field"
-      >
-        <label>
-          visibility
-        </label>
-        <VisibilitySelector
-          className="little-spacer-top"
-          onChange={[Function]}
-          visibility="public"
-        />
-        <div
-          className="spacer-top"
-        >
-          <UpgradeOrganizationBox
-            organization="org"
-          />
-        </div>
-      </div>
-      <div
-        className="modal-field big-spacer-top"
-      >
-        <label
-          htmlFor="create-project-branch"
-        >
-          branch
-        </label>
-        <input
-          autoFocus={true}
-          id="create-project-branch"
-          maxLength={200}
-          name="branch"
-          onChange={[Function]}
-          type="text"
-          value=""
-        />
-      </div>
-    </div>
-    <footer
-      className="modal-foot"
-    >
-      <SubmitButton
-        disabled={false}
-        id="create-project-submit"
-      >
-        create
-      </SubmitButton>
-      <ResetButtonLink
-        id="create-project-cancel"
-        onClick={[MockFunction]}
-      >
-        cancel
-      </ResetButtonLink>
-    </footer>
-  </form>
-</Modal>
-`;
index 4042b4ed3d13c50745fe2508979da966b385e5a6..b54a82c8fb182340a1ef89c484c9e9ade27bce47 100644 (file)
@@ -28,6 +28,7 @@ import VulnerabilityIcon from '../icons-components/VulnerabilityIcon';
 import { BranchLike } from '../../app/types';
 import { isShortLivingBranch, isPullRequest, isLongLivingBranch } from '../../helpers/branches';
 import { translateWithParameters } from '../../helpers/l10n';
+import { formatMeasure } from '../../helpers/measures';
 import './BranchStatus.css';
 
 interface Props {
@@ -43,16 +44,25 @@ export default function BranchStatus({ branchLike, concise = false }: Props) {
 
     const totalIssues =
       branchLike.status.bugs + branchLike.status.vulnerabilities + branchLike.status.codeSmells;
-    const indicatorColor = getQualityGateColor(branchLike.status.qualityGateStatus);
-    const shouldDisplayHelper = branchLike.status.qualityGateStatus === 'OK' && totalIssues > 0;
+    const status = branchLike.status.qualityGateStatus;
+    const indicatorColor = getQualityGateColor(status);
+    const shouldDisplayHelper = status === 'OK' && totalIssues > 0;
+
+    const label =
+      translateWithParameters('overview.quality_gate_x', formatMeasure(status, 'LEVEL')) +
+      (status !== 'OK'
+        ? ' ' + translateWithParameters('overview.quality_gate_failed_with_x', totalIssues)
+        : '');
 
     return concise ? (
-      <ul className="branch-status">
-        <li>{totalIssues}</li>
-        <li className="spacer-left">
-          <StatusIndicator color={indicatorColor} size="small" />
-        </li>
-      </ul>
+      <Tooltip overlay={label} placement="right">
+        <ul className="branch-status">
+          <li>{totalIssues}</li>
+          <li className="spacer-left">
+            <StatusIndicator color={indicatorColor} size="small" />
+          </li>
+        </ul>
+      </Tooltip>
     ) : (
       <ul className="branch-status">
         <li className="little-spacer-right">
index 67f4acdf5a4bdec83301fb10a2574fd3932dddaa..97f73111a99b9cdeb8c8a744f8ad9df1cb8c9682 100644 (file)
@@ -2287,6 +2287,7 @@ system.version_is_availble={version} is available
 #------------------------------------------------------------------------------
 overview.quality_gate=Quality Gate
 overview.quality_gate_x=Quality Gate: {0}
+overview.quality_gate_failed_with_x=with {0} errors
 overview.you_should_define_quality_gate=You should define a quality gate on this project.
 overview.quality_gate.ignored_conditions=Some Quality Gate conditions on New Code were ignored because of the small number of New Lines
 overview.quality_gate.ignored_conditions.tooltip=At the start of a leak period, if very few lines have been added or modified, it might be difficult to reach the desired level of code coverage or duplications. To prevent Quality Gate failure when there's little that can be done about it, Quality Gate conditions about duplications in new code and coverage on new code are ignored until the number of new lines is at least 20.