diff options
author | Mathieu Suen <mathieu.suen@sonarsource.com> | 2024-03-18 12:20:14 +0100 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2024-03-18 20:02:30 +0000 |
commit | a43209f6b80a8c7138f7ed1b74e4138f5238b5cd (patch) | |
tree | 07f9c6e72ef901c2eb8621e51bfa998a26697cd7 /server/sonar-web/src/main | |
parent | 03600808205c4f6df77713dbd40ee0a6e9c25a48 (diff) | |
download | sonarqube-a43209f6b80a8c7138f7ed1b74e4138f5238b5cd.tar.gz sonarqube-a43209f6b80a8c7138f7ed1b74e4138f5238b5cd.zip |
SONAR-21808 Add the failed badge on software quality cards
Diffstat (limited to 'server/sonar-web/src/main')
7 files changed, 61 insertions, 21 deletions
diff --git a/server/sonar-web/src/main/js/apps/overview/branches/OverallCodeMeasuresPanel.tsx b/server/sonar-web/src/main/js/apps/overview/branches/OverallCodeMeasuresPanel.tsx index 5530d595dac..d0ab2c0b250 100644 --- a/server/sonar-web/src/main/js/apps/overview/branches/OverallCodeMeasuresPanel.tsx +++ b/server/sonar-web/src/main/js/apps/overview/branches/OverallCodeMeasuresPanel.tsx @@ -70,6 +70,7 @@ export default function OverallCodeMeasuresPanel(props: Readonly<OverallCodeMeas <SoftwareImpactMeasureCard branch={branch} component={component} + conditions={conditions} softwareQuality={SoftwareQuality.Security} ratingMetricKey={MetricKey.security_rating} measures={measures} @@ -77,6 +78,7 @@ export default function OverallCodeMeasuresPanel(props: Readonly<OverallCodeMeas <SoftwareImpactMeasureCard branch={branch} component={component} + conditions={conditions} softwareQuality={SoftwareQuality.Reliability} ratingMetricKey={MetricKey.reliability_rating} measures={measures} @@ -84,6 +86,7 @@ export default function OverallCodeMeasuresPanel(props: Readonly<OverallCodeMeas <SoftwareImpactMeasureCard branch={branch} component={component} + conditions={conditions} softwareQuality={SoftwareQuality.Maintainability} ratingMetricKey={MetricKey.sqale_rating} measures={measures} diff --git a/server/sonar-web/src/main/js/apps/overview/branches/SoftwareImpactMeasureCard.tsx b/server/sonar-web/src/main/js/apps/overview/branches/SoftwareImpactMeasureCard.tsx index 9115a05f775..b98ec5f9d90 100644 --- a/server/sonar-web/src/main/js/apps/overview/branches/SoftwareImpactMeasureCard.tsx +++ b/server/sonar-web/src/main/js/apps/overview/branches/SoftwareImpactMeasureCard.tsx @@ -20,9 +20,9 @@ import styled from '@emotion/styled'; import { LinkHighlight, LinkStandalone } from '@sonarsource/echoes-react'; import classNames from 'classnames'; -import { BasicSeparator, LightGreyCard, TextBold, TextSubdued } from 'design-system'; +import { Badge, BasicSeparator, LightGreyCard, TextBold, TextSubdued } from 'design-system'; import * as React from 'react'; -import { useIntl } from 'react-intl'; +import { FormattedMessage, useIntl } from 'react-intl'; import Tooltip from '../../../components/controls/Tooltip'; import { DEFAULT_ISSUES_QUERY } from '../../../components/shared/utils'; import { @@ -39,14 +39,16 @@ import { SoftwareQuality, } from '../../../types/clean-code-taxonomy'; import { MetricKey, MetricType } from '../../../types/metrics'; +import { QualityGateStatusConditionEnhanced } from '../../../types/quality-gates'; import { Component, MeasureEnhanced } from '../../../types/types'; import { OverviewDisabledLinkTooltip } from '../components/OverviewDisabledLinkTooltip'; -import { softwareQualityToMeasure } from '../utils'; +import { Status, softwareQualityToMeasure } from '../utils'; import SoftwareImpactMeasureBreakdownCard from './SoftwareImpactMeasureBreakdownCard'; import SoftwareImpactMeasureRating from './SoftwareImpactMeasureRating'; export interface SoftwareImpactBreakdownCardProps { component: Component; + conditions: QualityGateStatusConditionEnhanced[]; softwareQuality: SoftwareQuality; ratingMetricKey: MetricKey; measures: MeasureEnhanced[]; @@ -54,7 +56,7 @@ export interface SoftwareImpactBreakdownCardProps { } export function SoftwareImpactMeasureCard(props: Readonly<SoftwareImpactBreakdownCardProps>) { - const { component, softwareQuality, ratingMetricKey, measures, branch } = props; + const { component, conditions, softwareQuality, ratingMetricKey, measures, branch } = props; const intl = useIntl(); @@ -92,12 +94,21 @@ export function SoftwareImpactMeasureCard(props: Readonly<SoftwareImpactBreakdow intl.formatMessage({ id: 'overview.measures.software_impact.count_tooltip' }) ); + const failed = conditions.some((c) => c.level === Status.ERROR && c.metric === ratingMetricKey); + return ( <LightGreyCard data-testid={`overview__software-impact-card-${softwareQuality}`} className="sw-w-1/3 sw-overflow-hidden sw-rounded-2 sw-p-4 sw-flex-col" > - <TextBold name={intl.formatMessage({ id: `software_quality.${softwareQuality}` })} /> + <div className="sw-flex sw-justify-between"> + <TextBold name={intl.formatMessage({ id: `software_quality.${softwareQuality}` })} /> + {failed && ( + <Badge className="sw-h-fit" variant="deleted"> + <FormattedMessage id="overview.measures.failed_badge" /> + </Badge> + )} + </div> <BasicSeparator className="sw--mx-4" /> <div className="sw-flex sw-flex-col sw-gap-3"> <div className="sw-flex sw-mt-2"> diff --git a/server/sonar-web/src/main/js/apps/overview/branches/__tests__/ActivityPanel-it.tsx b/server/sonar-web/src/main/js/apps/overview/branches/__tests__/ActivityPanel-it.tsx index c76a55708f3..45dddc7b81e 100644 --- a/server/sonar-web/src/main/js/apps/overview/branches/__tests__/ActivityPanel-it.tsx +++ b/server/sonar-web/src/main/js/apps/overview/branches/__tests__/ActivityPanel-it.tsx @@ -46,7 +46,7 @@ it('should render correctly', async () => { 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('v1.0')).toBeInTheDocument(); expect(screen.getByText(/event.category.OTHER/)).toBeInTheDocument(); expect(screen.getByText(/event.category.DEFINITION_CHANGE/)).toBeInTheDocument(); expect(screen.getByText('event.sqUpgrade10.2')).toBeInTheDocument(); diff --git a/server/sonar-web/src/main/js/apps/overview/branches/__tests__/BranchOverview-it.tsx b/server/sonar-web/src/main/js/apps/overview/branches/__tests__/BranchOverview-it.tsx index d4ffff4011f..8a7067c81d2 100644 --- a/server/sonar-web/src/main/js/apps/overview/branches/__tests__/BranchOverview-it.tsx +++ b/server/sonar-web/src/main/js/apps/overview/branches/__tests__/BranchOverview-it.tsx @@ -262,6 +262,21 @@ describe('project overview', () => { // eslint-disable-next-line jest/expect-expect it('should render software impact measure cards', async () => { + qualityGatesHandler.setQualityGateProjectStatus( + mockQualityGateProjectStatus({ + status: 'ERROR', + conditions: [ + { + actualValue: '2', + comparator: 'GT', + errorThreshold: '1', + metricKey: MetricKey.reliability_rating, + periodIndex: 1, + status: 'ERROR', + }, + ], + }), + ); const { user, ui } = getPageObjects(); renderBranchOverview(); @@ -288,6 +303,8 @@ describe('project overview', () => { [SoftwareImpactSeverity.Low]: 1, }, [false, true, false], + undefined, + true, ); ui.expectSoftwareImpactMeasureCard( SoftwareQuality.Maintainability, diff --git a/server/sonar-web/src/main/js/apps/overview/branches/test-utils.ts b/server/sonar-web/src/main/js/apps/overview/branches/test-utils.ts index 21925f6e066..17914108eeb 100644 --- a/server/sonar-web/src/main/js/apps/overview/branches/test-utils.ts +++ b/server/sonar-web/src/main/js/apps/overview/branches/test-utils.ts @@ -40,7 +40,16 @@ export const getPageObjects = () => { data?: SoftwareImpactMeasureData, severitiesActiveState?: boolean[], branch = 'master', + failed = false, ) => { + if (failed) { + expect( + byTestId(`overview__software-impact-card-${softwareQuality}`) + .byText('overview.measures.failed_badge') + .get(), + ).toBeInTheDocument(); + } + if (typeof rating === 'string') { expect( byText(rating, { exact: true }).get(ui.softwareImpactMeasureCard(softwareQuality).get()), diff --git a/server/sonar-web/src/main/js/apps/projects/components/project-card/__tests__/ProjectCard-test.tsx b/server/sonar-web/src/main/js/apps/projects/components/project-card/__tests__/ProjectCard-test.tsx index 3167be6de62..a81aab52c17 100644 --- a/server/sonar-web/src/main/js/apps/projects/components/project-card/__tests__/ProjectCard-test.tsx +++ b/server/sonar-web/src/main/js/apps/projects/components/project-card/__tests__/ProjectCard-test.tsx @@ -65,7 +65,7 @@ it('should display tags', async () => { it('should display private badge', () => { const project: Project = { ...PROJECT, visibility: Visibility.Private }; renderProjectCard(project); - expect(screen.getByLabelText('visibility.private')).toBeInTheDocument(); + expect(screen.getByText('visibility.private')).toBeInTheDocument(); }); it('should display configure analysis button for logged in user and scan rights', () => { @@ -81,7 +81,7 @@ it('should not display configure analysis button for logged in user and without it('should display applications', () => { renderProjectCard({ ...PROJECT, qualifier: ComponentQualifier.Application }); - expect(screen.getByLabelText('qualifier.APP')).toBeInTheDocument(); + expect(screen.getAllByText('qualifier.APP')).toHaveLength(2); }); it('should not display awaiting analysis badge and do not display old measures', () => { @@ -97,7 +97,7 @@ it('should not display awaiting analysis badge and do not display old measures', [MetricKey.vulnerabilities]: '6', }, }); - expect(screen.queryByRole('status', { name: 'projects.awaiting_scan' })).not.toBeInTheDocument(); + expect(screen.queryByText('projects.awaiting_scan')).not.toBeInTheDocument(); expect(screen.getByText('1')).toBeInTheDocument(); expect(screen.getByText('2')).toBeInTheDocument(); expect(screen.getByText('3')).toBeInTheDocument(); @@ -116,10 +116,10 @@ it('should display awaiting analysis badge and show the old measures', async () [MetricKey.vulnerabilities]: '6', }, }); - expect(screen.getByRole('status', { name: 'projects.awaiting_scan' })).toBeInTheDocument(); - await expect( - screen.getByRole('status', { name: 'projects.awaiting_scan' }), - ).toHaveATooltipWithContent('projects.awaiting_scan.description.TRK'); + expect(screen.getByText('projects.awaiting_scan')).toBeInTheDocument(); + await expect(screen.getByText('projects.awaiting_scan')).toHaveATooltipWithContent( + 'projects.awaiting_scan.description.TRK', + ); expect(screen.getByText('4')).toBeInTheDocument(); expect(screen.getByText('5')).toBeInTheDocument(); expect(screen.getByText('6')).toBeInTheDocument(); @@ -136,10 +136,10 @@ it('should display awaiting analysis badge and show the old measures for Applica [MetricKey.vulnerabilities]: '6', }, }); - expect(screen.getByRole('status', { name: 'projects.awaiting_scan' })).toBeInTheDocument(); - await expect( - screen.getByRole('status', { name: 'projects.awaiting_scan' }), - ).toHaveATooltipWithContent('projects.awaiting_scan.description.APP'); + expect(screen.getByText('projects.awaiting_scan')).toBeInTheDocument(); + await expect(screen.getByText('projects.awaiting_scan')).toHaveATooltipWithContent( + 'projects.awaiting_scan.description.APP', + ); expect(screen.getByText('4')).toBeInTheDocument(); expect(screen.getByText('5')).toBeInTheDocument(); expect(screen.getByText('6')).toBeInTheDocument(); @@ -150,7 +150,7 @@ it('should not display awaiting analysis badge if project is not analyzed', () = ...PROJECT, analysisDate: undefined, }); - expect(screen.queryByRole('status', { name: 'projects.awaiting_scan' })).not.toBeInTheDocument(); + expect(screen.queryByText('projects.awaiting_scan')).not.toBeInTheDocument(); }); it('should not display awaiting analysis badge if project does not have lines of code', () => { @@ -160,12 +160,12 @@ it('should not display awaiting analysis badge if project does not have lines of ...(({ [MetricKey.ncloc]: _, ...rest }) => rest)(MEASURES), }, }); - expect(screen.queryByRole('status', { name: 'projects.awaiting_scan' })).not.toBeInTheDocument(); + expect(screen.queryByText('projects.awaiting_scan')).not.toBeInTheDocument(); }); it('should not display awaiting analysis badge if it is a new code filter', () => { renderProjectCard(PROJECT, undefined, 'leak'); - expect(screen.queryByRole('status', { name: 'projects.awaiting_scan' })).not.toBeInTheDocument(); + expect(screen.queryByText('projects.awaiting_scan')).not.toBeInTheDocument(); }); it('should display 3 aplication', () => { diff --git a/server/sonar-web/src/main/js/components/issue/__tests__/Issue-it.tsx b/server/sonar-web/src/main/js/components/issue/__tests__/Issue-it.tsx index d116e14daf8..ce90ff69537 100644 --- a/server/sonar-web/src/main/js/components/issue/__tests__/Issue-it.tsx +++ b/server/sonar-web/src/main/js/components/issue/__tests__/Issue-it.tsx @@ -71,7 +71,7 @@ describe('rendering', () => { it('should render correctly for external rule engines', () => { renderIssue({ issue: mockIssue(true, { externalRuleEngine: 'ESLINT' }) }); - expect(screen.getByRole('status', { name: 'ESLINT' })).toBeInTheDocument(); + expect(screen.getByText('ESLINT')).toBeInTheDocument(); }); it('should render the SonarLint icon correctly', async () => { |