]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-19157 Remove extra bottom separator for Application failed conditions
authorDavid Cho-Lerat <david.cho-lerat@sonarsource.com>
Wed, 26 Jul 2023 14:15:33 +0000 (16:15 +0200)
committersonartech <sonartech@sonarsource.com>
Wed, 26 Jul 2023 20:03:25 +0000 (20:03 +0000)
server/sonar-web/src/main/js/apps/overview/branches/QualityGatePanel.tsx
server/sonar-web/src/main/js/apps/overview/branches/QualityGatePanelSection.tsx
server/sonar-web/src/main/js/apps/overview/branches/__tests__/QualityGatePanelSection-test.tsx [new file with mode: 0644]

index bdea28e3c36b06c56652b847432d03730511c5fa..c31b5cc7f42764d64f408226cf778074a729d38b 100644 (file)
@@ -17,6 +17,7 @@
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
+
 import { BasicSeparator, Card, DeferredSpinner } from 'design-system';
 import { flatMap } from 'lodash';
 import * as React from 'react';
@@ -49,18 +50,22 @@ export function QualityGatePanel(props: QualityGatePanelProps) {
   const overallLevel = qgStatuses.map((s) => s.status).includes('ERROR') ? 'ERROR' : 'OK';
   const success = overallLevel === 'OK';
 
+  const failedQgStatuses = qgStatuses.filter((qgStatus) => qgStatus.failedConditions.length > 0);
+
   const overallFailedConditionsCount = qgStatuses.reduce(
     (acc, qgStatus) => acc + qgStatus.failedConditions.length,
     0
   );
 
-  const nonCaycProjectsInApp = isApplication(component.qualifier)
+  const isApp = isApplication(component.qualifier);
+
+  const nonCaycProjectsInApp = isApp
     ? qgStatuses
         .filter(({ caycStatus }) => caycStatus === CaycStatus.NonCompliant)
         .sort(({ name: a }, { name: b }) => a.localeCompare(b, undefined, { sensitivity: 'base' }))
     : [];
 
-  const overCompliantCaycProjectsInApp = isApplication(component.qualifier)
+  const overCompliantCaycProjectsInApp = isApp
     ? qgStatuses
         .filter(({ caycStatus }) => caycStatus === CaycStatus.OverCompliant)
         .sort(({ name: a }, { name: b }) => a.localeCompare(b, undefined, { sensitivity: 'base' }))
@@ -91,12 +96,12 @@ export function QualityGatePanel(props: QualityGatePanelProps) {
 
               {!success && <BasicSeparator />}
 
-              {(overallFailedConditionsCount > 0 ||
-                qgStatuses.some(({ caycStatus }) => caycStatus !== CaycStatus.Compliant)) && (
+              {overallFailedConditionsCount > 0 && (
                 <div data-test="overview__quality-gate-conditions">
-                  {qgStatuses.map((qgStatus) => (
+                  {failedQgStatuses.map((qgStatus, qgStatusIdx) => (
                     <QualityGatePanelSection
-                      component={component}
+                      isApplication={isApp}
+                      isLastStatus={qgStatusIdx === failedQgStatuses.length - 1}
                       key={qgStatus.key}
                       qgStatus={qgStatus}
                     />
@@ -124,7 +129,7 @@ export function QualityGatePanel(props: QualityGatePanelProps) {
 
       {qgStatuses.length === 1 &&
         qgStatuses[0].caycStatus === CaycStatus.NonCompliant &&
-        !isApplication(component.qualifier) && (
+        !isApp && (
           <Card className="sw-mt-4 sw-body-sm">
             <CleanAsYouCodeWarning component={component} />
           </Card>
@@ -132,7 +137,7 @@ export function QualityGatePanel(props: QualityGatePanelProps) {
 
       {qgStatuses.length === 1 &&
         qgStatuses[0].caycStatus === CaycStatus.OverCompliant &&
-        !isApplication(component.qualifier) && (
+        !isApp && (
           <Card className="sw-mt-4 sw-body-sm">
             <CleanAsYouCodeWarningOverCompliant component={component} />
           </Card>
index daed4ad5e35cbdec04cd1eb934edd194a7baf75b..6a3379d9de9e03e4f7ac63ab6dd7d5c78d4d6406 100644 (file)
@@ -23,17 +23,16 @@ import * as React from 'react';
 import { translate, translateWithParameters } from '../../../helpers/l10n';
 import { isDiffMetric } from '../../../helpers/measures';
 import { BranchLike } from '../../../types/branch-like';
-import { isApplication } from '../../../types/component';
 import {
   QualityGateStatus,
   QualityGateStatusConditionEnhanced,
 } from '../../../types/quality-gates';
-import { CaycStatus, Component } from '../../../types/types';
 import QualityGateConditions from '../components/QualityGateConditions';
 
 export interface QualityGatePanelSectionProps {
   branchLike?: BranchLike;
-  component: Pick<Component, 'key' | 'qualifier' | 'qualityGate'>;
+  isApplication?: boolean;
+  isLastStatus?: boolean;
   qgStatus: QualityGateStatus;
 }
 
@@ -55,37 +54,19 @@ function splitConditions(
 }
 
 export function QualityGatePanelSection(props: QualityGatePanelSectionProps) {
-  const { component, qgStatus } = props;
+  const { isApplication, isLastStatus, qgStatus } = props;
   const [collapsed, setCollapsed] = React.useState(false);
 
   const toggle = React.useCallback(() => {
     setCollapsed(!collapsed);
   }, [collapsed]);
 
-  /*
-   * Show if project has failed conditions or that
-   * it is a single non-cayc project
-   * In the context of an App, only show projects with failed conditions
-   */
-  if (
-    !(
-      qgStatus.failedConditions.length > 0 ||
-      (qgStatus.caycStatus !== CaycStatus.Compliant && !isApplication(component.qualifier))
-    )
-  ) {
-    return null;
-  }
-
   const [newCodeFailedConditions, overallFailedConditions] = splitConditions(
     qgStatus.failedConditions
   );
 
-  const collapsible = isApplication(component.qualifier);
-
   const showSectionTitles =
-    isApplication(component.qualifier) ||
-    qgStatus.caycStatus !== CaycStatus.Compliant ||
-    (overallFailedConditions.length > 0 && newCodeFailedConditions.length > 0);
+    isApplication || (overallFailedConditions.length > 0 && newCodeFailedConditions.length > 0);
 
   const toggleLabel = collapsed
     ? translateWithParameters('overview.quality_gate.show_project_conditions_x', qgStatus.name)
@@ -151,7 +132,7 @@ export function QualityGatePanelSection(props: QualityGatePanelSectionProps) {
 
   return (
     <>
-      {collapsible ? (
+      {isApplication ? (
         <>
           <Accordion
             ariaLabel={toggleLabel}
@@ -175,7 +156,8 @@ export function QualityGatePanelSection(props: QualityGatePanelSectionProps) {
 
             {renderFailedConditions()}
           </Accordion>
-          <BasicSeparator />
+
+          {(!isLastStatus || collapsed) && <BasicSeparator />}
         </>
       ) : (
         renderFailedConditions()
diff --git a/server/sonar-web/src/main/js/apps/overview/branches/__tests__/QualityGatePanelSection-test.tsx b/server/sonar-web/src/main/js/apps/overview/branches/__tests__/QualityGatePanelSection-test.tsx
new file mode 100644 (file)
index 0000000..05d84f0
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+import { screen } from '@testing-library/react';
+import * as React from 'react';
+import { renderComponent } from '../../../../helpers/testReactTestingUtils';
+import { MetricKey } from '../../../../types/metrics';
+import { CaycStatus, Status } from '../../../../types/types';
+import QualityGatePanelSection, { QualityGatePanelSectionProps } from '../QualityGatePanelSection';
+
+const failedConditions = [
+  {
+    level: 'ERROR' as Status,
+    measure: {
+      metric: {
+        id: 'metricId1',
+        key: 'metricKey1',
+        name: 'metricName1',
+        type: 'metricType1',
+      },
+    },
+    metric: MetricKey.new_coverage,
+    op: 'op1',
+  },
+  {
+    level: 'ERROR' as Status,
+    measure: {
+      metric: {
+        id: 'metricId2',
+        key: 'metricKey2',
+        name: 'metricName2',
+        type: 'metricType2',
+      },
+    },
+    metric: MetricKey.security_hotspots,
+    op: 'op2',
+  },
+];
+
+const qgStatus = {
+  caycStatus: CaycStatus.Compliant,
+  failedConditions,
+  key: 'qgStatusKey',
+  name: 'qgStatusName',
+  status: 'ERROR' as Status,
+};
+
+it('should render correctly for an application with 1 new code condition and 1 overall code condition', async () => {
+  renderQualityGatePanelSection();
+
+  expect(await screen.findByText('quality_gates.conditions.new_code_1')).toBeInTheDocument();
+  expect(await screen.findByText('quality_gates.conditions.overall_code_1')).toBeInTheDocument();
+});
+
+it('should render correctly for a project with 1 new code condition', () => {
+  renderQualityGatePanelSection({
+    isApplication: false,
+    qgStatus: { ...qgStatus, failedConditions: [failedConditions[0]] },
+  });
+
+  expect(screen.queryByText('quality_gates.conditions.new_code_1')).not.toBeInTheDocument();
+  expect(screen.queryByText('quality_gates.conditions.overall_code_1')).not.toBeInTheDocument();
+});
+
+function renderQualityGatePanelSection(props: Partial<QualityGatePanelSectionProps> = {}) {
+  return renderComponent(<QualityGatePanelSection isApplication qgStatus={qgStatus} {...props} />);
+}