]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-21515 Project card in projects list adopts Clean Code taxonomy
authorstanislavh <stanislav.honcharov@sonarsource.com>
Thu, 25 Jan 2024 15:35:30 +0000 (16:35 +0100)
committersonartech <sonartech@sonarsource.com>
Wed, 31 Jan 2024 20:03:36 +0000 (20:03 +0000)
server/sonar-web/src/main/js/api/measures.ts
server/sonar-web/src/main/js/apps/projects/components/__tests__/AllProjects-test.tsx
server/sonar-web/src/main/js/apps/projects/components/project-card/ProjectCardMeasures.tsx
server/sonar-web/src/main/js/apps/projects/components/project-card/__tests__/ProjectCardMeasures-test.tsx
server/sonar-web/src/main/js/apps/projects/utils.ts
sonar-core/src/main/resources/org/sonar/l10n/core.properties

index 68c784baabe48d7557b5acab534e2eb3a41793fa..faa195c339b7a1989b492f7e90d97b8d3351e5a8 100644 (file)
@@ -151,12 +151,42 @@ export async function getMeasuresWithPeriodAndMetrics(
   return result;
 }
 
-export function getMeasuresForProjects(
+export async function getMeasuresForProjects(
   projectKeys: string[],
   metricKeys: string[],
 ): Promise<MeasuresForProjects[]> {
-  return getJSON('/api/measures/search', {
+  // TODO: Remove this mock (SONAR-21488)
+  const mockedMetrics = metricKeys.filter(
+    (metric) =>
+      ![
+        MetricKey.maintainability_issues,
+        MetricKey.reliability_issues,
+        MetricKey.security_issues,
+      ].includes(metric as MetricKey),
+  );
+
+  const result = await getJSON('/api/measures/search', {
     projectKeys: projectKeys.join(),
-    metricKeys: metricKeys.join(),
+    metricKeys: mockedMetrics.join(),
   }).then((r) => r.measures);
+
+  [
+    MetricKey.maintainability_issues,
+    MetricKey.reliability_issues,
+    MetricKey.security_issues,
+  ].forEach((metric) => {
+    if (metricKeys.includes(metric)) {
+      projectKeys.forEach((projectKey) => {
+        result.push({
+          component: projectKey,
+          metric,
+          value: JSON.stringify({
+            total: 2,
+          }),
+        });
+      });
+    }
+  });
+
+  return result;
 }
index 13db6aa84dfbb27e804f4b79209133e1a7f1d5fa..9c70f662718905ab5e04b955d5422b9dfa4c9dd6 100644 (file)
@@ -92,7 +92,9 @@ it('changes sort and perspective', async () => {
   await user.click(screen.getByText('projects.view.new_code'));
 
   // each project should show "new bugs" instead of "bugs"
-  expect(await screen.findAllByLabelText(MetricKey.new_bugs)).toHaveLength(20);
+  expect(await screen.findAllByText(`metric.${MetricKey.new_violations}.description`)).toHaveLength(
+    20,
+  );
 
   expect(save).toHaveBeenCalledWith(LS_PROJECTS_VIEW, 'leak');
   // sort should also be updated
index bb867ec4dca330d20521cc9d0339643ed8acf8d4..cfe04620a01da096326b3b45172a6324f2ae8e53 100644 (file)
@@ -42,6 +42,28 @@ export interface ProjectCardMeasuresProps {
   componentQualifier: ComponentQualifier;
 }
 
+function renderNewIssues(props: ProjectCardMeasuresProps) {
+  const { measures, isNewCode } = props;
+
+  if (!isNewCode) {
+    return null;
+  }
+
+  return (
+    <ProjectCardMeasure
+      metricKey={MetricKey.new_violations}
+      label={translate(`metric.${MetricKey.new_violations}.description`)}
+    >
+      <Measure
+        metricKey={MetricKey.new_violations}
+        metricType={MetricType.ShortInteger}
+        value={measures[MetricKey.new_violations]}
+        className="sw-ml-2 sw-body-md-highlight"
+      />
+    </ProjectCardMeasure>
+  );
+}
+
 function renderCoverage(props: ProjectCardMeasuresProps) {
   const { measures, isNewCode } = props;
   const coverageMetric = isNewCode ? MetricKey.new_coverage : MetricKey.coverage;
@@ -93,20 +115,32 @@ function renderDuplication(props: ProjectCardMeasuresProps) {
 function renderRatings(props: ProjectCardMeasuresProps) {
   const { isNewCode, measures } = props;
 
+  const measuresByCodeLeak = isNewCode
+    ? []
+    : [
+        {
+          iconLabel: translate(`metric.${MetricKey.security_issues}.short_name`),
+          noShrink: true,
+          metricKey: MetricKey.security_issues,
+          metricRatingKey: MetricKey.security_rating,
+          metricType: MetricType.ShortInteger,
+        },
+        {
+          iconLabel: translate(`metric.${MetricKey.reliability_issues}.short_name`),
+          metricKey: MetricKey.reliability_issues,
+          metricRatingKey: MetricKey.reliability_rating,
+          metricType: MetricType.ShortInteger,
+        },
+        {
+          iconLabel: translate(`metric.${MetricKey.maintainability_issues}.short_name`),
+          metricKey: MetricKey.maintainability_issues,
+          metricRatingKey: MetricKey.sqale_rating,
+          metricType: MetricType.ShortInteger,
+        },
+      ];
+
   const measureList = [
-    {
-      iconLabel: translate('metric.bugs.name'),
-      noShrink: true,
-      metricKey: isNewCode ? MetricKey.new_bugs : MetricKey.bugs,
-      metricRatingKey: isNewCode ? MetricKey.new_reliability_rating : MetricKey.reliability_rating,
-      metricType: MetricType.ShortInteger,
-    },
-    {
-      iconLabel: translate('metric.vulnerabilities.name'),
-      metricKey: isNewCode ? MetricKey.new_vulnerabilities : MetricKey.vulnerabilities,
-      metricRatingKey: isNewCode ? MetricKey.new_security_rating : MetricKey.security_rating,
-      metricType: MetricType.ShortInteger,
-    },
+    ...measuresByCodeLeak,
     {
       iconKey: 'security_hotspots',
       iconLabel: translate('projects.security_hotspots_reviewed'),
@@ -118,25 +152,28 @@ function renderRatings(props: ProjectCardMeasuresProps) {
         : MetricKey.security_review_rating,
       metricType: MetricType.Percent,
     },
-    {
-      iconLabel: translate('metric.code_smells.name'),
-      metricKey: isNewCode ? MetricKey.new_code_smells : MetricKey.code_smells,
-      metricRatingKey: isNewCode ? MetricKey.new_maintainability_rating : MetricKey.sqale_rating,
-      metricType: MetricType.ShortInteger,
-    },
   ];
 
   return measureList.map((measure) => {
     const { iconLabel, metricKey, metricRatingKey, metricType } = measure;
     const value = formatRating(measures[metricRatingKey]);
 
+    const measureValue =
+      [
+        MetricKey.security_issues,
+        MetricKey.reliability_issues,
+        MetricKey.maintainability_issues,
+      ].includes(metricKey) && measures[metricKey]
+        ? JSON.parse(measures[metricKey] as string)?.total
+        : measures[metricKey];
+
     return (
       <ProjectCardMeasure key={metricKey} metricKey={metricKey} label={iconLabel}>
         <MetricsRatingBadge label={metricKey} rating={value as MetricsLabel} />
         <Measure
           metricKey={metricKey}
           metricType={metricType}
-          value={measures[metricKey]}
+          value={measureValue}
           className="sw-ml-2 sw-body-md-highlight"
         />
       </ProjectCardMeasure>
@@ -160,6 +197,7 @@ export default function ProjectCardMeasures(props: ProjectCardMeasuresProps) {
   }
 
   const measureList = [
+    renderNewIssues(props),
     ...renderRatings(props),
     renderCoverage(props),
     renderDuplication(props),
index 555de614547202e980593ebcffb5afad205dd9ab..04c1197884b339047297e3f033a74b813ebeb259 100644 (file)
@@ -33,7 +33,7 @@ jest.mock('date-fns', () => ({
 describe('Overall measures', () => {
   it('should be rendered properly', () => {
     renderProjectCardMeasures();
-    expect(screen.getByTitle('metric.bugs.name')).toBeInTheDocument();
+    expect(screen.getByTitle('metric.security_issues.short_name')).toBeInTheDocument();
   });
 
   it("should be not be rendered if there's no line of code", () => {
@@ -54,6 +54,7 @@ describe('New code measures', () => {
   it('should be rendered properly', () => {
     renderProjectCardMeasures({}, { isNewCode: true });
     expect(screen.getByLabelText(MetricKey.new_security_hotspots_reviewed)).toBeInTheDocument();
+    expect(screen.getByTitle('metric.new_violations.description')).toBeInTheDocument();
   });
 });
 
@@ -67,6 +68,9 @@ function renderProjectCardMeasures(
     [MetricKey.code_smells]: '132',
     [MetricKey.coverage]: '88.3',
     [MetricKey.duplicated_lines_density]: '9.8',
+    [MetricKey.maintainability_issues]: JSON.stringify({ total: 10 }),
+    [MetricKey.reliability_issues]: JSON.stringify({ total: 10 }),
+    [MetricKey.security_issues]: JSON.stringify({ total: 10 }),
     [MetricKey.ncloc]: '2053',
     [MetricKey.reliability_rating]: '1.0',
     [MetricKey.security_rating]: '1.0',
@@ -80,6 +84,7 @@ function renderProjectCardMeasures(
     [MetricKey.new_code_smells]: '0',
     [MetricKey.new_coverage]: '26.55',
     [MetricKey.new_duplicated_lines_density]: '0.55',
+    [MetricKey.new_violations]: '10',
     [MetricKey.new_lines]: '87',
     ...measuresOverride,
   };
index ad510ee5bd44d56c85815ff68c741a3925d4e17b..d3d02f16d9b5c14718728388ad791eb6d126d76d 100644 (file)
@@ -90,14 +90,14 @@ const PAGE_SIZE = 50;
 
 export const METRICS = [
   MetricKey.alert_status,
-  MetricKey.bugs,
+  MetricKey.reliability_issues,
   MetricKey.reliability_rating,
-  MetricKey.vulnerabilities,
+  MetricKey.security_issues,
   MetricKey.security_rating,
+  MetricKey.maintainability_issues,
+  MetricKey.sqale_rating,
   MetricKey.security_hotspots_reviewed,
   MetricKey.security_review_rating,
-  MetricKey.code_smells,
-  MetricKey.sqale_rating,
   MetricKey.duplicated_lines_density,
   MetricKey.coverage,
   MetricKey.ncloc,
@@ -107,14 +107,9 @@ export const METRICS = [
 
 export const LEAK_METRICS = [
   MetricKey.alert_status,
-  MetricKey.new_bugs,
-  MetricKey.new_reliability_rating,
-  MetricKey.new_vulnerabilities,
-  MetricKey.new_security_rating,
+  MetricKey.new_violations,
   MetricKey.new_security_hotspots_reviewed,
   MetricKey.new_security_review_rating,
-  MetricKey.new_code_smells,
-  MetricKey.new_maintainability_rating,
   MetricKey.new_coverage,
   MetricKey.new_duplicated_lines_density,
   MetricKey.new_lines,
index 397344fa01b897e83646d41fc6b3d02619e6549e..45b83dc14cee76c88d76f6334865b84ace5f24e3 100644 (file)
@@ -3274,10 +3274,13 @@ metric.high_impact_accepted_issues.name=High Impact Accepted Issues
 metric.high_impact_accepted_issues.description=Accepted issues with high impact
 metric.maintainability_issues.name=Maintainability Issues
 metric.maintainability_issues.description=Maintainability issues
+metric.maintainability_issues.short_name=Maintainability
 metric.reliability_issues.name=Reliability Issues
 metric.reliability_issues.description=Reliability issues
+metric.reliability_issues.short_name=Reliability
 metric.security_issues.name=Security Issues
 metric.security_issues.description=Security issues
+metric.security_issues.short_name=Security
 
 #------------------------------------------------------------------------------
 #