From a43209f6b80a8c7138f7ed1b74e4138f5238b5cd Mon Sep 17 00:00:00 2001 From: Mathieu Suen Date: Mon, 18 Mar 2024 12:20:14 +0100 Subject: [PATCH] SONAR-21808 Add the failed badge on software quality cards --- .../design-system/src/components/Badge.tsx | 7 ++--- .../src/components/__tests__/Badge-test.tsx | 10 +++++-- .../branches/OverallCodeMeasuresPanel.tsx | 3 ++ .../branches/SoftwareImpactMeasureCard.tsx | 21 ++++++++++---- .../branches/__tests__/ActivityPanel-it.tsx | 2 +- .../branches/__tests__/BranchOverview-it.tsx | 17 +++++++++++ .../js/apps/overview/branches/test-utils.ts | 9 ++++++ .../__tests__/ProjectCard-test.tsx | 28 +++++++++---------- .../components/issue/__tests__/Issue-it.tsx | 2 +- .../resources/org/sonar/l10n/core.properties | 4 +-- 10 files changed, 73 insertions(+), 30 deletions(-) diff --git a/server/sonar-web/design-system/src/components/Badge.tsx b/server/sonar-web/design-system/src/components/Badge.tsx index 54ed4d62289..1746a417d0c 100644 --- a/server/sonar-web/design-system/src/components/Badge.tsx +++ b/server/sonar-web/design-system/src/components/Badge.tsx @@ -32,8 +32,7 @@ const variantList: Record = { counterFailed: 'badgeCounterFailed', }; -interface BadgeProps { - children: string | number; +interface BadgeProps extends React.PropsWithChildren { className?: string; title?: string; variant?: BadgeVariant; @@ -41,9 +40,9 @@ interface BadgeProps { export function Badge({ className, children, title, variant = 'default' }: BadgeProps) { const commonProps = { - 'aria-label': title ?? children.toString(), + 'aria-label': title, className, - role: 'status', + role: title ? 'img' : 'presentation', title, }; diff --git a/server/sonar-web/design-system/src/components/__tests__/Badge-test.tsx b/server/sonar-web/design-system/src/components/__tests__/Badge-test.tsx index 97f901852d8..5429ccc2a16 100644 --- a/server/sonar-web/design-system/src/components/__tests__/Badge-test.tsx +++ b/server/sonar-web/design-system/src/components/__tests__/Badge-test.tsx @@ -23,10 +23,14 @@ import { Badge } from '../Badge'; it('renders badge correctly', () => { render(foo); - expect(screen.getByRole('status')).toBeInTheDocument(); + expect(screen.getByText('foo')).toBeInTheDocument(); }); it('renders counter correctly', () => { - render(23); - expect(screen.getByRole('status')).toHaveAttribute('aria-label', '23'); + render( + + 23 + , + ); + expect(screen.getByRole('img')).toHaveAccessibleName('This 23'); }); 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) { - 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 c.level === Status.ERROR && c.metric === ratingMetricKey); + return ( - +
+ + {failed && ( + + + + )} +
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 () => { diff --git a/sonar-core/src/main/resources/org/sonar/l10n/core.properties b/sonar-core/src/main/resources/org/sonar/l10n/core.properties index 8286d2f65aa..2e222a4f985 100644 --- a/sonar-core/src/main/resources/org/sonar/l10n/core.properties +++ b/sonar-core/src/main/resources/org/sonar/l10n/core.properties @@ -3930,7 +3930,7 @@ overview.accepted_issues.total=Total accepted issues overview.high_impact_accepted_issues=High impact accepted issues overview.measures.empty_explanation=Measures on New Code will appear after the second analysis of this branch. overview.measures.empty_link={learn_more_link} about the Clean as You Code approach. -overview.measures.same_reference.explanation=This branch is configured to use itself as reference branch. It will never have New Code. +overview.measures.same_reference.explanation=This branch is configured to use itself as a reference branch. It will never have New Code. overview.measures.bad_reference.explanation=This branch could not be compared to its reference branch. See the SCM or analysis report for more details. overview.measures.bad_setting.link=This can be fixed in the {setting_link} setting. overview.measures.security_hotspots_reviewed=Reviewed @@ -4018,7 +4018,7 @@ overview.sonarlint_ad.details_3=Repair flagged issues in real-time with quick fi overview.sonarlint_ad.details_4=12 major IDE's supported (including key JetBrains and Microsoft IDE's overview.sonarlint_ad.details_5=Free forever overview.sonarlint_ad.learn_more=Learn more about SonarLint -overview.sonarlint_ad.close_promotion=Close SonarLint romotion +overview.sonarlint_ad.close_promotion=Close SonarLint promotion overview.badges.get_badge=Badges overview.badges.title=Get project badges -- 2.39.5