]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-10457 Display correct quality gate status on short lived branches
authorGrégoire Aubert <gregoire.aubert@sonarsource.com>
Thu, 8 Mar 2018 10:33:10 +0000 (11:33 +0100)
committerGuillaume Jambet <guillaume.jambet@gmail.com>
Wed, 14 Mar 2018 12:30:35 +0000 (13:30 +0100)
15 files changed:
server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNavBranch-test.tsx
server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNavBranchesMenu-test.tsx
server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNavBranchesMenuItem-test.tsx
server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNavMeta-test.tsx
server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavBranch-test.tsx.snap
server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavBranchesMenu-test.tsx.snap
server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavBranchesMenuItem-test.tsx.snap
server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavMeta-test.tsx.snap
server/sonar-web/src/main/js/app/types.ts
server/sonar-web/src/main/js/components/common/BranchStatus.tsx
server/sonar-web/src/main/js/components/common/StatusIndicator.css
server/sonar-web/src/main/js/components/common/__tests__/BranchStatus-test.tsx
server/sonar-web/src/main/js/components/common/__tests__/__snapshots__/BranchStatus-test.tsx.snap
server/sonar-web/src/main/js/helpers/__tests__/branches-test.ts
sonar-core/src/main/resources/org/sonar/l10n/core.properties

index 7eac067d8dc2bb40ae78e5d1acc7849866a7f678..5258daf383f663a15e0978b6357c52ada66354ae 100644 (file)
@@ -52,7 +52,7 @@ it('renders short-living branch', () => {
     isMain: false,
     mergeBranch: 'master',
     name: 'foo',
-    status: { bugs: 0, codeSmells: 0, vulnerabilities: 0 },
+    status: { bugs: 0, codeSmells: 0, qualityGateStatus: 'OK', vulnerabilities: 0 },
     type: BranchType.SHORT
   };
   const component = {} as Component;
index bb43ec60c63fb7bddd9d036270bd6166bb8b424e..5a318153fb99316a51ac128d9ccd775bc3661556 100644 (file)
@@ -94,7 +94,7 @@ function shortBranch(name: string, isOrphan?: true): ShortLivingBranch {
     isOrphan,
     mergeBranch: 'master',
     name,
-    status: { bugs: 0, codeSmells: 0, vulnerabilities: 0 },
+    status: { bugs: 0, codeSmells: 0, qualityGateStatus: 'OK', vulnerabilities: 0 },
     type: BranchType.SHORT
   };
 }
@@ -108,7 +108,7 @@ function pullRequest(title: string): PullRequest {
     base: 'master',
     branch: 'feature',
     key: '1234',
-    status: { bugs: 0, codeSmells: 0, vulnerabilities: 0 },
+    status: { bugs: 0, codeSmells: 0, qualityGateStatus: 'OK', vulnerabilities: 0 },
     title
   };
 }
index 0635bea82a8cc16c4b6b8d5a9aa38cc4bcb02e94..01995e7f92dd0b3c0509b6389e12e21dfd7fdc94 100644 (file)
@@ -28,7 +28,7 @@ const shortBranch: ShortLivingBranch = {
   isMain: false,
   mergeBranch: 'master',
   name: 'foo',
-  status: { bugs: 1, codeSmells: 2, vulnerabilities: 3 },
+  status: { bugs: 1, codeSmells: 2, qualityGateStatus: 'ERROR', vulnerabilities: 3 },
   type: BranchType.SHORT
 };
 
index 62bdbdbb71506a5c82e987ab9599a124415df05a..c6bff99c4a90aa6d514859c3d55263ba35c77404 100644 (file)
@@ -37,7 +37,7 @@ it('renders status of short-living branch', () => {
     isMain: false,
     mergeBranch: 'master',
     name: 'feature',
-    status: { bugs: 0, codeSmells: 2, vulnerabilities: 3 },
+    status: { bugs: 0, codeSmells: 2, qualityGateStatus: 'ERROR', vulnerabilities: 3 },
     type: BranchType.SHORT
   };
   expect(
@@ -74,7 +74,7 @@ it('renders meta for pull request', () => {
     base: 'master',
     branch: 'feature',
     key: '1234',
-    status: { bugs: 0, codeSmells: 2, vulnerabilities: 3 },
+    status: { bugs: 0, codeSmells: 2, qualityGateStatus: 'ERROR', vulnerabilities: 3 },
     title: 'Feature PR',
     url: 'https://example.com/pull/1234'
   };
index 6e45772bb56d8efed86f290fca7ff7c093817a27..5f53fb0b98300723defa4ae95b73c9f1620ec0a9 100644 (file)
@@ -152,6 +152,7 @@ exports[`renders short-living branch 1`] = `
           "status": Object {
             "bugs": 0,
             "codeSmells": 0,
+            "qualityGateStatus": "OK",
             "vulnerabilities": 0,
           },
           "type": "SHORT",
index cc51efd1976e7fc35b31ad4bcab97f374b9102fe..b30b921ae6b947d1cc72f537833e9b0ca93261d9 100644 (file)
@@ -55,6 +55,7 @@ exports[`renders list 1`] = `
             "status": Object {
               "bugs": 0,
               "codeSmells": 0,
+              "qualityGateStatus": "OK",
               "vulnerabilities": 0,
             },
             "title": "qux",
@@ -99,6 +100,7 @@ exports[`renders list 1`] = `
             "status": Object {
               "bugs": 0,
               "codeSmells": 0,
+              "qualityGateStatus": "OK",
               "vulnerabilities": 0,
             },
             "type": "SHORT",
@@ -127,6 +129,7 @@ exports[`renders list 1`] = `
             "status": Object {
               "bugs": 0,
               "codeSmells": 0,
+              "qualityGateStatus": "OK",
               "vulnerabilities": 0,
             },
             "type": "SHORT",
@@ -195,6 +198,7 @@ exports[`renders list 1`] = `
             "status": Object {
               "bugs": 0,
               "codeSmells": 0,
+              "qualityGateStatus": "OK",
               "vulnerabilities": 0,
             },
             "type": "SHORT",
@@ -250,6 +254,7 @@ exports[`searches 1`] = `
             "status": Object {
               "bugs": 0,
               "codeSmells": 0,
+              "qualityGateStatus": "OK",
               "vulnerabilities": 0,
             },
             "type": "SHORT",
index 781da6de591e9721d9497fc38128bac15e0313b0..df7c8da7b472b5608da7373063738d089d8a15c1 100644 (file)
@@ -97,6 +97,7 @@ exports[`renders short-living branch 1`] = `
               "status": Object {
                 "bugs": 1,
                 "codeSmells": 2,
+                "qualityGateStatus": "ERROR",
                 "vulnerabilities": 3,
               },
               "type": "SHORT",
@@ -118,6 +119,7 @@ exports[`renders short-living branch 1`] = `
               "status": Object {
                 "bugs": 1,
                 "codeSmells": 2,
+                "qualityGateStatus": "ERROR",
                 "vulnerabilities": 3,
               },
               "type": "SHORT",
@@ -169,6 +171,7 @@ exports[`renders short-living orhpan branch 1`] = `
               "status": Object {
                 "bugs": 1,
                 "codeSmells": 2,
+                "qualityGateStatus": "ERROR",
                 "vulnerabilities": 3,
               },
               "type": "SHORT",
@@ -191,6 +194,7 @@ exports[`renders short-living orhpan branch 1`] = `
               "status": Object {
                 "bugs": 1,
                 "codeSmells": 2,
+                "qualityGateStatus": "ERROR",
                 "vulnerabilities": 3,
               },
               "type": "SHORT",
index 46e1c4becd7f15b3c951d235953bfd7ca8196225..06f96d32ee37417120e8399028ae48b31c40b317 100644 (file)
@@ -61,6 +61,7 @@ exports[`renders meta for pull request 1`] = `
           "status": Object {
             "bugs": 0,
             "codeSmells": 2,
+            "qualityGateStatus": "ERROR",
             "vulnerabilities": 3,
           },
           "title": "Feature PR",
@@ -95,6 +96,7 @@ exports[`renders status of short-living branch 1`] = `
           "status": Object {
             "bugs": 0,
             "codeSmells": 2,
+            "qualityGateStatus": "ERROR",
             "vulnerabilities": 3,
           },
           "type": "SHORT",
index 106d76e679f200f9f670efc05c9b4b7f783ab7c6..922cbf8cf692aa77df6cea35cd0c969bc744baa9 100644 (file)
@@ -360,6 +360,7 @@ export interface PullRequest {
   status?: {
     bugs: number;
     codeSmells: number;
+    qualityGateStatus: string;
     vulnerabilities: number;
   };
   title: string;
@@ -440,6 +441,7 @@ export interface ShortLivingBranch extends Branch {
   status?: {
     bugs: number;
     codeSmells: number;
+    qualityGateStatus: string;
     vulnerabilities: number;
   };
   type: BranchType.SHORT;
index d2e3f9929738dc4c0dcbd61bc0c98c0136ad631c..b2e401121124d296b46c826ce0dbb38be5845a31 100644 (file)
@@ -22,9 +22,12 @@ import StatusIndicator from './StatusIndicator';
 import Level from '../ui/Level';
 import BugIcon from '../icons-components/BugIcon';
 import CodeSmellIcon from '../icons-components/CodeSmellIcon';
+import HelpIcon from '../icons-components/HelpIcon';
+import Tooltip from '../controls/Tooltip';
 import VulnerabilityIcon from '../icons-components/VulnerabilityIcon';
 import { BranchLike } from '../../app/types';
 import { isShortLivingBranch, isPullRequest, isLongLivingBranch } from '../../helpers/branches';
+import { translateWithParameters } from '../../helpers/l10n';
 import './BranchStatus.css';
 
 interface Props {
@@ -40,8 +43,8 @@ export default function BranchStatus({ branchLike, concise = false }: Props) {
 
     const totalIssues =
       branchLike.status.bugs + branchLike.status.vulnerabilities + branchLike.status.codeSmells;
-
-    const indicatorColor = totalIssues > 0 ? 'red' : 'green';
+    const indicatorColor = getQualityGateColor(branchLike.status.qualityGateStatus);
+    const shouldDisplayHelper = branchLike.status.qualityGateStatus === 'OK' && totalIssues > 0;
 
     return concise ? (
       <ul className="branch-status">
@@ -52,21 +55,33 @@ export default function BranchStatus({ branchLike, concise = false }: Props) {
       </ul>
     ) : (
       <ul className="branch-status">
-        <li className="spacer-right">
+        <li className="little-spacer-right">
           <StatusIndicator color={indicatorColor} size="small" />
         </li>
         <li className="spacer-left">
           {branchLike.status.bugs}
-          <BugIcon />
+          <BugIcon className="little-spacer-left" />
         </li>
         <li className="spacer-left">
           {branchLike.status.vulnerabilities}
-          <VulnerabilityIcon />
+          <VulnerabilityIcon className="little-spacer-left" />
         </li>
         <li className="spacer-left">
           {branchLike.status.codeSmells}
-          <CodeSmellIcon />
+          <CodeSmellIcon className="little-spacer-left" />
         </li>
+        {shouldDisplayHelper && (
+          <Tooltip
+            overlay={translateWithParameters(
+              'branches.short_lived.quality_gate.description',
+              totalIssues
+            )}
+            placement="right">
+            <li className="spacer-left">
+              <HelpIcon className="text-info" />
+            </li>
+          </Tooltip>
+        )}
       </ul>
     );
   } else if (isLongLivingBranch(branchLike)) {
@@ -79,3 +94,15 @@ export default function BranchStatus({ branchLike, concise = false }: Props) {
     return null;
   }
 }
+
+function getQualityGateColor(status: string) {
+  let indicatorColor = 'gray';
+  if (status === 'ERROR') {
+    indicatorColor = 'red';
+  } else if (status === 'WARN') {
+    indicatorColor = 'orange';
+  } else if (status === 'OK') {
+    indicatorColor = 'green';
+  }
+  return indicatorColor;
+}
index a7e86044211c5b71e9b8432626d30543ae51f68d..9d0949597ac1771fcd0f24810e14419acf325797 100644 (file)
 .status-indicator.green {
   background-color: var(--green);
 }
+
+.status-indicator.orange {
+  background-color: var(--orange);
+}
+
+.status-indicator.gray {
+  background-color: var(--gray71);
+}
index 12aef45991925b617ab150cbbe72567b20e2adb3..516d2f6c222b6a75b61df1663bce0bee725d4003 100644 (file)
@@ -23,16 +23,22 @@ import BranchStatus from '../BranchStatus';
 import { BranchType, LongLivingBranch, ShortLivingBranch } from '../../../app/types';
 
 it('renders status of short-living branches', () => {
-  checkShort(0, 0, 0);
-  checkShort(0, 1, 0);
-  checkShort(7, 3, 6);
+  checkShort('OK', 0, 0, 0);
+  checkShort('WARN', 0, 1, 0);
+  checkShort('ERROR', 7, 3, 6);
+  checkShort('OK', 0, 0, 1);
 
-  function checkShort(bugs: number, codeSmells: number, vulnerabilities: number) {
+  function checkShort(
+    qualityGateStatus: string,
+    bugs: number,
+    codeSmells: number,
+    vulnerabilities: number
+  ) {
     const shortBranch: ShortLivingBranch = {
       isMain: false,
       mergeBranch: 'master',
       name: 'foo',
-      status: { bugs, codeSmells, vulnerabilities },
+      status: { bugs, codeSmells, qualityGateStatus, vulnerabilities },
       type: BranchType.SHORT
     };
     expect(shallow(<BranchStatus branchLike={shortBranch} />)).toMatchSnapshot();
index d648e77db49a45938cf6939227b6b93b1691e879..e10d3e2826b76cc5de7dc0e70ef7c97068c399d3 100644 (file)
@@ -19,7 +19,7 @@ exports[`renders status of short-living branches 1`] = `
   className="branch-status"
 >
   <li
-    className="spacer-right"
+    className="little-spacer-right"
   >
     <StatusIndicator
       color="green"
@@ -30,19 +30,25 @@ exports[`renders status of short-living branches 1`] = `
     className="spacer-left"
   >
     0
-    <BugIcon />
+    <BugIcon
+      className="little-spacer-left"
+    />
   </li>
   <li
     className="spacer-left"
   >
     0
-    <VulnerabilityIcon />
+    <VulnerabilityIcon
+      className="little-spacer-left"
+    />
   </li>
   <li
     className="spacer-left"
   >
     0
-    <CodeSmellIcon />
+    <CodeSmellIcon
+      className="little-spacer-left"
+    />
   </li>
 </ul>
 `;
@@ -52,10 +58,10 @@ exports[`renders status of short-living branches 2`] = `
   className="branch-status"
 >
   <li
-    className="spacer-right"
+    className="little-spacer-right"
   >
     <StatusIndicator
-      color="red"
+      color="orange"
       size="small"
     />
   </li>
@@ -63,19 +69,25 @@ exports[`renders status of short-living branches 2`] = `
     className="spacer-left"
   >
     0
-    <BugIcon />
+    <BugIcon
+      className="little-spacer-left"
+    />
   </li>
   <li
     className="spacer-left"
   >
     0
-    <VulnerabilityIcon />
+    <VulnerabilityIcon
+      className="little-spacer-left"
+    />
   </li>
   <li
     className="spacer-left"
   >
     1
-    <CodeSmellIcon />
+    <CodeSmellIcon
+      className="little-spacer-left"
+    />
   </li>
 </ul>
 `;
@@ -85,7 +97,7 @@ exports[`renders status of short-living branches 3`] = `
   className="branch-status"
 >
   <li
-    className="spacer-right"
+    className="little-spacer-right"
   >
     <StatusIndicator
       color="red"
@@ -96,19 +108,76 @@ exports[`renders status of short-living branches 3`] = `
     className="spacer-left"
   >
     7
-    <BugIcon />
+    <BugIcon
+      className="little-spacer-left"
+    />
   </li>
   <li
     className="spacer-left"
   >
     6
-    <VulnerabilityIcon />
+    <VulnerabilityIcon
+      className="little-spacer-left"
+    />
   </li>
   <li
     className="spacer-left"
   >
     3
-    <CodeSmellIcon />
+    <CodeSmellIcon
+      className="little-spacer-left"
+    />
+  </li>
+</ul>
+`;
+
+exports[`renders status of short-living branches 4`] = `
+<ul
+  className="branch-status"
+>
+  <li
+    className="little-spacer-right"
+  >
+    <StatusIndicator
+      color="green"
+      size="small"
+    />
   </li>
+  <li
+    className="spacer-left"
+  >
+    0
+    <BugIcon
+      className="little-spacer-left"
+    />
+  </li>
+  <li
+    className="spacer-left"
+  >
+    1
+    <VulnerabilityIcon
+      className="little-spacer-left"
+    />
+  </li>
+  <li
+    className="spacer-left"
+  >
+    0
+    <CodeSmellIcon
+      className="little-spacer-left"
+    />
+  </li>
+  <Tooltip
+    overlay="branches.short_lived.quality_gate.description.1"
+    placement="right"
+  >
+    <li
+      className="spacer-left"
+    >
+      <HelpIcon
+        className="text-info"
+      />
+    </li>
+  </Tooltip>
 </ul>
 `;
index abf8bd874383c998ed8cdce9f1baf6215f598275..934ef060ecca438b58ed5d625eb22574fc930be1 100644 (file)
@@ -78,8 +78,12 @@ describe('#isSameBranchLike', () => {
   });
 
   it('compares pull requests', () => {
-    expect(isSameBranchLike(pullRequest({ key: '1234' }), pullRequest({ key: '1234' }))).toBeTruthy();
-    expect(isSameBranchLike(pullRequest({ key: '1234' }), pullRequest({ key: '5678' }))).toBeFalsy();
+    expect(
+      isSameBranchLike(pullRequest({ key: '1234' }), pullRequest({ key: '1234' }))
+    ).toBeTruthy();
+    expect(
+      isSameBranchLike(pullRequest({ key: '1234' }), pullRequest({ key: '5678' }))
+    ).toBeFalsy();
   });
 
   it('compares branches', () => {
@@ -103,7 +107,7 @@ function mainBranch(): MainBranch {
 }
 
 function shortLivingBranch(overrides?: Partial<ShortLivingBranch>): ShortLivingBranch {
-  const status = { bugs: 0, codeSmells: 0, vulnerabilities: 0 };
+  const status = { bugs: 0, codeSmells: 0, qualityGateStatus: 'OK', vulnerabilities: 0 };
   return {
     isMain: false,
     mergeBranch: 'master',
@@ -120,7 +124,7 @@ function longLivingBranch(overrides?: Partial<LongLivingBranch>): LongLivingBran
 }
 
 function pullRequest(overrides?: Partial<PullRequest>): PullRequest {
-  const status = { bugs: 0, codeSmells: 0, vulnerabilities: 0 };
+  const status = { bugs: 0, codeSmells: 0, qualityGateStatus: 'OK', vulnerabilities: 0 };
   return {
     base: 'master',
     branch: 'feature',
index f5b5fb14f0217de1322a103d9ee71fcf42fc08ab..caf4db6930431d003ba6adabeb052962c258d52a 100644 (file)
@@ -2650,6 +2650,7 @@ branches.no_support.header=Get the most out of SonarQube with branches analysis
 branches.no_support.header.text=Analyze each branch of your project separately with the Developer Edition.
 branches.search_for_branches=Search for branches...
 branches.pull_requests=Pull Requests
+branches.short_lived.quality_gate.description=The branch status is passed because there are no open issue. The remaining {0} issue(s) have been confirmed.
 branches.short_lived_branches=Short-lived branches
 branches.pull_request.for_merge_into_x_from_y=for merge into {base} from {branch}
 branches.see_the_pr=See the PR