]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-17816 Warn user about non-cayc-compliant QG on the project overview
authorJeremy Davis <jeremy.davis@sonarsource.com>
Tue, 10 Jan 2023 13:35:17 +0000 (14:35 +0100)
committersonartech <sonartech@sonarsource.com>
Thu, 12 Jan 2023 20:02:51 +0000 (20:02 +0000)
server/sonar-web/src/main/js/apps/overview/branches/QualityGatePanelSection.tsx
server/sonar-web/src/main/js/apps/overview/branches/__tests__/BranchOverview-test.tsx
server/sonar-web/src/main/js/apps/overview/branches/__tests__/QualityGatePanelSection-test.tsx
server/sonar-web/src/main/js/apps/overview/branches/__tests__/__snapshots__/QualityGatePanelSection-test.tsx.snap
server/sonar-web/src/main/js/apps/overview/styles.css
server/sonar-web/src/main/js/apps/overview/utils.ts
sonar-core/src/main/resources/org/sonar/l10n/core.properties

index d67432add26951266ac005b818c17f25859e8497..ac9bc24303029667e800cacb09a7150e1350d43c 100644 (file)
@@ -21,18 +21,16 @@ import * as React from 'react';
 import { ButtonPlain } from '../../../components/controls/buttons';
 import ChevronDownIcon from '../../../components/icons/ChevronDownIcon';
 import ChevronRightIcon from '../../../components/icons/ChevronRightIcon';
-import { Alert } from '../../../components/ui/Alert';
 import { translate, translateWithParameters } from '../../../helpers/l10n';
 import { isDiffMetric } from '../../../helpers/measures';
 import { BranchLike } from '../../../types/branch-like';
-import { ComponentQualifier } from '../../../types/component';
+import { isApplication } from '../../../types/component';
 import {
   QualityGateStatus,
   QualityGateStatusConditionEnhanced,
 } from '../../../types/quality-gates';
 import { Component } from '../../../types/types';
 import QualityGateConditions from '../components/QualityGateConditions';
-import { CAYC_METRICS } from '../utils';
 import CleanAsYouCodeWarning from './CleanAsYouCodeWarning';
 
 export interface QualityGatePanelSectionProps {
@@ -41,22 +39,21 @@ export interface QualityGatePanelSectionProps {
   qgStatus: QualityGateStatus;
 }
 
-function splitConditions(conditions: QualityGateStatusConditionEnhanced[]) {
-  const caycConditions = [];
+function splitConditions(
+  conditions: QualityGateStatusConditionEnhanced[]
+): [QualityGateStatusConditionEnhanced[], QualityGateStatusConditionEnhanced[]] {
   const newCodeFailedConditions = [];
   const overallFailedConditions = [];
 
   for (const condition of conditions) {
-    if (CAYC_METRICS.includes(condition.metric)) {
-      caycConditions.push(condition);
-    } else if (isDiffMetric(condition.metric)) {
+    if (isDiffMetric(condition.metric)) {
       newCodeFailedConditions.push(condition);
     } else {
       overallFailedConditions.push(condition);
     }
   }
 
-  return [caycConditions, newCodeFailedConditions, overallFailedConditions];
+  return [newCodeFailedConditions, overallFailedConditions];
 }
 
 function displayConditions(conditions: number) {
@@ -84,24 +81,15 @@ export function QualityGatePanelSection(props: QualityGatePanelSectionProps) {
     return null;
   }
 
-  const [caycConditions, newCodeFailedConditions, overallFailedConditions] = splitConditions(
+  const [newCodeFailedConditions, overallFailedConditions] = splitConditions(
     qgStatus.failedConditions
   );
 
-  /*
-   * Show Clean as You Code if:
-   * - The QG is not CAYC-compliant
-   * - There are *any* failing conditions, we either show:
-   *   - the cayc-specific failures
-   *   - that cayc is passing and only other conditions are failing
-   */
-  const showCayc = !qgStatus.isCaycCompliant || qgStatus.failedConditions.length > 0;
+  const showName = isApplication(component.qualifier);
 
-  const showSuccessfulCayc = caycConditions.length === 0 && qgStatus.isCaycCompliant;
-
-  const hasOtherConditions = newCodeFailedConditions.length + overallFailedConditions.length > 0;
-
-  const showName = component.qualifier === ComponentQualifier.Application;
+  const showSectionTitles =
+    !qgStatus.isCaycCompliant ||
+    (overallFailedConditions.length > 0 && newCodeFailedConditions.length > 0);
 
   const toggleLabel = collapsed
     ? translateWithParameters('overview.quality_gate.show_project_conditions_x', qgStatus.name)
@@ -131,69 +119,45 @@ export function QualityGatePanelSection(props: QualityGatePanelSectionProps) {
 
       {!collapsed && (
         <>
-          {showCayc && (
-            <>
-              <div className="display-flex-center overview-quality-gate-conditions-section-title">
-                <h4 className="padded">{translate('quality_gates.conditions.cayc')}</h4>
-                {displayConditions(caycConditions.length)}
-              </div>
-
-              {!qgStatus.isCaycCompliant && (
-                <div className="big-padded bordered-bottom overview-quality-gate-conditions-list">
-                  <CleanAsYouCodeWarning />
-                </div>
-              )}
-
-              {showSuccessfulCayc && (
-                <div className="big-padded bordered-bottom overview-quality-gate-conditions-list">
-                  <Alert variant="success" className="no-margin-bottom">
-                    {translate('overview.quality_gate.conditions.cayc.passed')}
-                  </Alert>
-                </div>
-              )}
+          {!qgStatus.isCaycCompliant && (
+            <div className="big-padded bordered-bottom overview-quality-gate-conditions-list">
+              <CleanAsYouCodeWarning />
+            </div>
+          )}
 
-              {caycConditions.length > 0 && (
-                <QualityGateConditions
-                  component={qgStatus}
-                  branchLike={qgStatus.branchLike}
-                  failedConditions={caycConditions}
-                />
+          {newCodeFailedConditions.length > 0 && (
+            <>
+              {showSectionTitles && (
+                <h4 className="big-padded overview-quality-gate-conditions-section-title">
+                  {translateWithParameters(
+                    'quality_gates.conditions.new_code_x',
+                    newCodeFailedConditions.length.toString()
+                  )}
+                </h4>
               )}
+              <QualityGateConditions
+                component={qgStatus}
+                branchLike={qgStatus.branchLike}
+                failedConditions={newCodeFailedConditions}
+              />
             </>
           )}
 
-          {hasOtherConditions && (
+          {overallFailedConditions.length > 0 && (
             <>
-              <div className="display-flex-center overview-quality-gate-conditions-section-title">
-                <h4 className="padded">{translate('quality_gates.conditions.other_conditions')}</h4>
-                {displayConditions(newCodeFailedConditions.length + overallFailedConditions.length)}
-              </div>
-
-              {newCodeFailedConditions.length > 0 && (
-                <>
-                  <h5 className="big-padded overview-quality-gate-conditions-subsection-title">
-                    {translate('quality_gates.conditions.new_code')}
-                  </h5>
-                  <QualityGateConditions
-                    component={qgStatus}
-                    branchLike={qgStatus.branchLike}
-                    failedConditions={newCodeFailedConditions}
-                  />
-                </>
-              )}
-
-              {overallFailedConditions.length > 0 && (
-                <>
-                  <h5 className="big-padded overview-quality-gate-conditions-subsection-title">
-                    {translate('quality_gates.conditions.overall_code')}
-                  </h5>
-                  <QualityGateConditions
-                    component={qgStatus}
-                    branchLike={qgStatus.branchLike}
-                    failedConditions={overallFailedConditions}
-                  />
-                </>
+              {showSectionTitles && (
+                <h4 className="big-padded overview-quality-gate-conditions-section-title">
+                  {translateWithParameters(
+                    'quality_gates.conditions.overall_code_x',
+                    overallFailedConditions.length.toString()
+                  )}
+                </h4>
               )}
+              <QualityGateConditions
+                component={qgStatus}
+                branchLike={qgStatus.branchLike}
+                failedConditions={overallFailedConditions}
+              />
             </>
           )}
         </>
index be4171f53e2192c14a8b9006cbf7685319c2ec84..7b0e14a91f4a4244486bde99464468ec60996a07 100644 (file)
@@ -34,7 +34,6 @@ import { mockQualityGateProjectStatus } from '../../../../helpers/mocks/quality-
 import { mockLoggedInUser, mockPeriod } from '../../../../helpers/testMocks';
 import { renderComponent } from '../../../../helpers/testReactTestingUtils';
 import { ComponentQualifier } from '../../../../types/component';
-import { MetricKey } from '../../../../types/metrics';
 import { GraphType } from '../../../../types/project-activity';
 import { Measure, Metric } from '../../../../types/types';
 import BranchOverview, { BRANCH_OVERVIEW_ACTIVITY_GRAPH, NO_CI_DETECTED } from '../BranchOverview';
@@ -231,32 +230,6 @@ describe('project overview', () => {
 
     expect(await screen.findByText('metric.level.ERROR')).toBeInTheDocument();
     expect(screen.getByText('overview.X_conditions_failed.2')).toBeInTheDocument();
-
-    expect(
-      screen.queryByText('overview.quality_gate.conditions.cayc.passed')
-    ).not.toBeInTheDocument();
-  });
-
-  it('should show a failed QG with passing CAYC conditions', async () => {
-    jest.mocked(getQualityGateProjectStatus).mockResolvedValueOnce(
-      mockQualityGateProjectStatus({
-        status: 'ERROR',
-        conditions: [
-          {
-            actualValue: '12',
-            comparator: 'GT',
-            errorThreshold: '10',
-            metricKey: MetricKey.new_bugs,
-            periodIndex: 1,
-            status: 'ERROR',
-          },
-        ],
-      })
-    );
-    renderBranchOverview();
-
-    expect(await screen.findByText('metric.level.ERROR')).toBeInTheDocument();
-    expect(screen.getByText('overview.quality_gate.conditions.cayc.passed')).toBeInTheDocument();
   });
 
   it('should correctly show a project as empty', async () => {
index 251e2730e72e0a792f84bd0a52d57ca400ea1fb7..6d151b5bff10d230ab92375e0fb33b7432ea9fb3 100644 (file)
@@ -55,6 +55,7 @@ function shallowRender(props: Partial<QualityGatePanelSectionProps> = {}) {
           mockQualityGateStatusConditionEnhanced({ metric: MetricKey.new_bugs }),
         ],
         status: 'ERROR',
+        isCaycCompliant: false,
       })}
       {...props}
     />
index c49d543555015054d5f15a74eab1c2ca75870ad8..dd37526a134e137552f2f2b34a922eb857bb307a 100644 (file)
@@ -4,44 +4,16 @@ exports[`should render correctly 1`] = `
 <div
   className="overview-quality-gate-conditions"
 >
-  <div
-    className="display-flex-center overview-quality-gate-conditions-section-title"
-  >
-    <h4
-      className="padded"
-    >
-      quality_gates.conditions.cayc
-    </h4>
-  </div>
   <div
     className="big-padded bordered-bottom overview-quality-gate-conditions-list"
   >
-    <Alert
-      className="no-margin-bottom"
-      variant="success"
-    >
-      overview.quality_gate.conditions.cayc.passed
-    </Alert>
-  </div>
-  <div
-    className="display-flex-center overview-quality-gate-conditions-section-title"
-  >
-    <h4
-      className="padded"
-    >
-      quality_gates.conditions.other_conditions
-    </h4>
-    <span
-      className="text-muted big-spacer-left"
-    >
-      overview.X_conditions_failed.2
-    </span>
+    <CleanAsYouCodeWarning />
   </div>
-  <h5
-    className="big-padded overview-quality-gate-conditions-subsection-title"
+  <h4
+    className="big-padded overview-quality-gate-conditions-section-title"
   >
-    quality_gates.conditions.new_code
-  </h5>
+    quality_gates.conditions.new_code_x.1
+  </h4>
   <Memo(QualityGateConditions)
     component={
       {
@@ -94,7 +66,7 @@ exports[`should render correctly 1`] = `
           },
         ],
         "ignoredConditions": false,
-        "isCaycCompliant": true,
+        "isCaycCompliant": false,
         "key": "foo",
         "name": "Foo",
         "status": "ERROR",
@@ -128,11 +100,11 @@ exports[`should render correctly 1`] = `
       ]
     }
   />
-  <h5
-    className="big-padded overview-quality-gate-conditions-subsection-title"
+  <h4
+    className="big-padded overview-quality-gate-conditions-section-title"
   >
-    quality_gates.conditions.overall_code
-  </h5>
+    quality_gates.conditions.overall_code_x.1
+  </h4>
   <Memo(QualityGateConditions)
     component={
       {
@@ -185,7 +157,7 @@ exports[`should render correctly 1`] = `
           },
         ],
         "ignoredConditions": false,
-        "isCaycCompliant": true,
+        "isCaycCompliant": false,
         "key": "foo",
         "name": "Foo",
         "status": "ERROR",
@@ -248,44 +220,16 @@ exports[`should render correctly 2`] = `
       </h3>
     </div>
   </ButtonPlain>
-  <div
-    className="display-flex-center overview-quality-gate-conditions-section-title"
-  >
-    <h4
-      className="padded"
-    >
-      quality_gates.conditions.cayc
-    </h4>
-  </div>
   <div
     className="big-padded bordered-bottom overview-quality-gate-conditions-list"
   >
-    <Alert
-      className="no-margin-bottom"
-      variant="success"
-    >
-      overview.quality_gate.conditions.cayc.passed
-    </Alert>
-  </div>
-  <div
-    className="display-flex-center overview-quality-gate-conditions-section-title"
-  >
-    <h4
-      className="padded"
-    >
-      quality_gates.conditions.other_conditions
-    </h4>
-    <span
-      className="text-muted big-spacer-left"
-    >
-      overview.X_conditions_failed.2
-    </span>
+    <CleanAsYouCodeWarning />
   </div>
-  <h5
-    className="big-padded overview-quality-gate-conditions-subsection-title"
+  <h4
+    className="big-padded overview-quality-gate-conditions-section-title"
   >
-    quality_gates.conditions.new_code
-  </h5>
+    quality_gates.conditions.new_code_x.1
+  </h4>
   <Memo(QualityGateConditions)
     component={
       {
@@ -338,7 +282,7 @@ exports[`should render correctly 2`] = `
           },
         ],
         "ignoredConditions": false,
-        "isCaycCompliant": true,
+        "isCaycCompliant": false,
         "key": "foo",
         "name": "Foo",
         "status": "ERROR",
@@ -372,11 +316,11 @@ exports[`should render correctly 2`] = `
       ]
     }
   />
-  <h5
-    className="big-padded overview-quality-gate-conditions-subsection-title"
+  <h4
+    className="big-padded overview-quality-gate-conditions-section-title"
   >
-    quality_gates.conditions.overall_code
-  </h5>
+    quality_gates.conditions.overall_code_x.1
+  </h4>
   <Memo(QualityGateConditions)
     component={
       {
@@ -429,7 +373,7 @@ exports[`should render correctly 2`] = `
           },
         ],
         "ignoredConditions": false,
-        "isCaycCompliant": true,
+        "isCaycCompliant": false,
         "key": "foo",
         "name": "Foo",
         "status": "ERROR",
index 17327125a321461a56369f96170fb71b8bbfd622..d06f711df3218d6a9a9ced1006949d4cf91eeac1 100644 (file)
   background: var(--barBorderColor);
 }
 
-.overview-quality-gate-conditions-subsection-title {
-  background-color: white;
-  border-bottom: 1px solid var(--barBorderColor);
-  margin: 0;
-  font-size: var(--baseFontSize);
-}
-
 .overview-quality-gate-conditions-list-collapse {
   margin: calc(2 * var(--gridSize)) 0;
 }
index 332a91fefc67f4608817e79f59fda4a248475ffa..e42daa229315d3d56d48fa9e4e652ad1df4ca432 100644 (file)
@@ -112,16 +112,6 @@ export enum MeasurementType {
   Duplication = 'DUPLICATION',
 }
 
-/*
- * Metrics part of Clean As You Code
- */
-export const CAYC_METRICS: string[] = [
-  MetricKey.new_maintainability_rating,
-  MetricKey.new_reliability_rating,
-  MetricKey.new_security_hotspots_reviewed,
-  MetricKey.new_security_rating,
-];
-
 const MEASUREMENTS_MAP = {
   [MeasurementType.Coverage]: {
     metric: MetricKey.coverage,
index 2a0b6c4eb27f93a453e25c1ac0711e8d665f71c0..1f2e922a0ba92f1e0ad7f7c9df37e44d49c5b038 100644 (file)
@@ -1809,14 +1809,14 @@ quality_gates.condition_deleted=Successfully deleted condition
 quality_gates.delete_condition.confirm.message=Are you sure you want to delete the "{0}" condition?
 quality_gates.conditions.fails_when=Quality Gate fails when
 quality_gates.conditions.metric=Metric
-quality_gates.conditions.cayc=Clean as You Code
-quality_gates.conditions.other_conditions=Other conditions
 quality_gates.conditions.new_code=On New Code
 quality_gates.conditions.new_code.long=Conditions on New Code
 quality_gates.conditions.new_code.description=Conditions on New Code apply to all branches and to Pull Requests.
+quality_gates.conditions.new_code_x={0} condition(s) failed on new code
 quality_gates.conditions.overall_code=On Overall Code
 quality_gates.conditions.overall_code.long=Conditions on Overall Code
 quality_gates.conditions.overall_code.description=Conditions on Overall Code apply to branches only.
+quality_gates.conditions.overall_code_x={0} condition(s) failed on overall code
 quality_gates.conditions.operator=Operator
 quality_gates.conditions.warning=Warning
 quality_gates.conditions.warning.tooltip=Warning status is deprecated and will disappear with the next update of the Quality Gate.
@@ -1870,7 +1870,7 @@ quality_gates.cayc.lock_edit=Lock for editing
 quality_gates.cayc.tooltip.ok.condition=This condition is compliant with Clean as You Code.
 quality_gates.cayc.tooltip.weak.condition=This condition is too permissive. Increase its value to follow Clean as You Code.
 quality_gates.cayc.tooltip.missing.condition=This condition is missing. Add it to follow Clean as You Code.
-quality_gates.cayc.tooltip.weak.quality_gate=Some Clean as You Code conditions are missing or are too permissive. 
+quality_gates.cayc.tooltip.weak.quality_gate=This Quality Gate does not follow the Clean as You Code methodology.  
 quality_gates.cayc.badge.tooltip.learn_more=Learn more: Clean as You Code
 quality_gates.cayc_condition.delete_warning=This condition is part of Clean as You Code. If you delete it you will not benefit from the power of Clean As You Code anymore.
 quality_gates.cayc_condition.edit_warning=This condition is part of Clean as You Code. If you decrease its value you will not benefit from the power of Clean As You Code anymore.
@@ -3268,10 +3268,9 @@ overview.you_should_define_quality_gate=You should define a quality gate on this
 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 new code 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. An administrator can disable this in the general settings.
 overview.quality_gate.conditions_on_new_code=Only conditions on new code that are defined in the Quality Gate are checked. See the {link} associated to the project for details.
-overview.quality_gate.conditions.cayc.warning=Some Clean as You Code conditions are missing or are too permissive.
+overview.quality_gate.conditions.cayc.warning=This Quality Gate does not comply with Clean as You Code
 overview.quality_gate.conditions.cayc.details=Clean as You Code conditions ensure that only Clean Code passes the gate.
-overview.quality_gate.conditions.cayc.link=What is Clean as You Code
-overview.quality_gate.conditions.cayc.passed=All conditions passed
+overview.quality_gate.conditions.cayc.link=Learn more: Clean as You Code
 overview.quality_gate.show_project_conditions_x=Show failed conditions for project {0}
 overview.quality_gate.hide_project_conditions_x=Hide failed conditions for project {0}
 overview.quality_profiles=Quality Profiles used