]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-20673 Improve the way we display the quality gate status for each analysis
authorAmbroise C <ambroise.christea@sonarsource.com>
Mon, 16 Oct 2023 14:20:09 +0000 (16:20 +0200)
committersonartech <sonartech@sonarsource.com>
Tue, 17 Oct 2023 20:02:44 +0000 (20:02 +0000)
server/sonar-web/src/main/js/apps/overview/branches/ActivityPanel.tsx
server/sonar-web/src/main/js/apps/overview/branches/Analysis.tsx
server/sonar-web/src/main/js/apps/overview/branches/__tests__/ActivityPanel-it.tsx
server/sonar-web/src/main/js/apps/overview/utils.ts

index a5f6c732e78419ad38f5df9ae70352fb1073fdc9..67ed4414dc15e84118b791bffd8131cb0f43422c 100644 (file)
@@ -32,6 +32,7 @@ import { parseDate } from '../../../helpers/dates';
 import { translate, translateWithParameters } from '../../../helpers/l10n';
 import { localizeMetric } from '../../../helpers/measures';
 import { BranchLike } from '../../../types/branch-like';
+import { MetricKey } from '../../../types/metrics';
 import {
   Analysis as AnalysisType,
   GraphType,
@@ -100,6 +101,15 @@ export function ActivityPanel(props: ActivityPanelProps) {
     [measuresHistory, analyses.length],
   );
 
+  const qualityGateStatuses = React.useMemo(
+    () =>
+      measuresHistory
+        .find(({ metric }) => metric === MetricKey.alert_status)
+        ?.history.slice(-MAX_ANALYSES_NB)
+        .reverse(),
+    [measuresHistory],
+  );
+
   return (
     <div className="sw-mt-8">
       <PageTitle as="h2" text={translate('overview.activity')} />
@@ -135,6 +145,7 @@ export function ActivityPanel(props: ActivityPanelProps) {
                   analysis={analysis}
                   isFirstAnalysis={index === analyses.length - 1}
                   qualifier={component.qualifier}
+                  qualityGateStatus={qualityGateStatuses?.[index]?.value}
                   variations={analysisVariations[index]}
                 />
                 {index !== displayedAnalyses.length - 1 && <BasicSeparator className="sw-my-3" />}
index 88507be61db33ea6e9cfe10f1c8c4c97d1ff7754..c46c684f2f495f04944a02b9de801a35a91bdaca 100644 (file)
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
+import { QualityGateIndicator } from 'design-system';
 import { sortBy } from 'lodash';
 import * as React from 'react';
+import { FormattedMessage } from 'react-intl';
 import DateTimeFormatter from '../../../components/intl/DateTimeFormatter';
 import { translate } from '../../../helpers/l10n';
 import { ComponentQualifier } from '../../../types/component';
@@ -27,6 +29,7 @@ import {
   ProjectAnalysisEventCategory,
   Analysis as TypeAnalysis,
 } from '../../../types/project-activity';
+import { Status } from '../../../types/types';
 import { AnalysisVariations } from './AnalysisVariations';
 import Event from './Event';
 
@@ -34,14 +37,15 @@ export interface AnalysisProps {
   analysis: TypeAnalysis;
   isFirstAnalysis?: boolean;
   qualifier: string;
+  qualityGateStatus?: string;
   variations?: AnalysisMeasuresVariations;
 }
 
 export function Analysis(props: Readonly<AnalysisProps>) {
-  const { analysis, isFirstAnalysis, qualifier, variations } = props;
+  const { analysis, isFirstAnalysis, qualifier, qualityGateStatus, variations } = props;
 
   const sortedEvents = sortBy(
-    analysis.events,
+    analysis.events.filter((event) => event.category !== ProjectAnalysisEventCategory.QualityGate),
     (event) => {
       switch (event.category) {
         case ProjectAnalysisEventCategory.Version:
@@ -66,8 +70,29 @@ export function Analysis(props: Readonly<AnalysisProps>) {
 
   return (
     <div className="sw-body-sm">
-      <div className="sw-body-sm-highlight sw-mb-1">
-        <DateTimeFormatter date={analysis.date} />
+      <div className="sw-flex sw-justify-between sw-mb-1">
+        <div className="sw-body-sm-highlight">
+          <DateTimeFormatter date={analysis.date} />
+        </div>
+        {qualityGateStatus !== undefined && (
+          <div className="sw-flex sw-items-center">
+            <FormattedMessage
+              id="overview.quality_gate_x"
+              values={{
+                0: (
+                  <QualityGateIndicator
+                    className="sw-mx-2"
+                    size="sm"
+                    status={qualityGateStatus as Status}
+                  />
+                ),
+              }}
+            />
+            <span className="sw-body-sm-highlight">
+              <FormattedMessage id={`metric.level.${qualityGateStatus}`} />
+            </span>
+          </div>
+        )}
       </div>
 
       {sortedEvents.length > 0
index 0c02104405e11b7839386067430ed21b9a6cc27e..5c83b9abd724bf5e8056b69d0630e782a14bd8f8 100644 (file)
@@ -40,7 +40,8 @@ import ActivityPanel, { ActivityPanelProps } from '../ActivityPanel';
 
 it('should render correctly', async () => {
   renderActivityPanel();
-  expect(await screen.findByText('event.quality_gate.ERROR')).toBeInTheDocument();
+  expect(await screen.findAllByText('metric.level.ERROR')).toHaveLength(2);
+  expect(screen.getAllByText('metric.level.OK')).toHaveLength(2);
   expect(screen.getByRole('status', { name: 'v1.0' })).toBeInTheDocument();
   expect(screen.getByText(/event.category.OTHER/)).toBeInTheDocument();
   expect(screen.getByText(/event.category.DEFINITION_CHANGE/)).toBeInTheDocument();
@@ -112,6 +113,15 @@ function renderActivityPanel(props: Partial<ActivityPanelProps> = {}) {
         mockHistoryItem({ date: parseDate('2020-10-27T18:33:50+0200'), value: '14.2' }),
       ],
     }),
+    mockMeasureHistory({
+      metric: MetricKey.alert_status,
+      history: [
+        mockHistoryItem({ date: parseDate('2018-10-27T10:21:15+0200'), value: 'OK' }),
+        mockHistoryItem({ date: parseDate('2018-10-27T12:21:15+0200'), value: 'ERROR' }),
+        mockHistoryItem({ date: parseDate('2020-10-27T16:33:50+0200'), value: 'ERROR' }),
+        mockHistoryItem({ date: parseDate('2020-10-27T18:33:50+0200'), value: 'OK' }),
+      ],
+    }),
   ];
   const mockedMetrics = [mockMetric()];
   const mockedAnalysis = [
index 195386a2a1aea695959b5b6fe7896351e8a4099f..d67c362653c133b235d01625ebfcbebd03229cf8 100644 (file)
@@ -106,6 +106,7 @@ export const HISTORY_METRICS_LIST: string[] = [
   MetricKey.duplicated_lines_density,
   MetricKey.ncloc,
   MetricKey.coverage,
+  MetricKey.alert_status,
 ];
 
 const MEASURES_VARIATIONS_METRICS = [