counterFailed: 'badgeCounterFailed',
};
-interface BadgeProps {
- children: string | number;
+interface BadgeProps extends React.PropsWithChildren {
className?: string;
title?: string;
variant?: BadgeVariant;
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,
};
it('renders badge correctly', () => {
render(<Badge>foo</Badge>);
- expect(screen.getByRole('status')).toBeInTheDocument();
+ expect(screen.getByText('foo')).toBeInTheDocument();
});
it('renders counter correctly', () => {
- render(<Badge variant="counter">23</Badge>);
- expect(screen.getByRole('status')).toHaveAttribute('aria-label', '23');
+ render(
+ <Badge title="This 23" variant="counter">
+ 23
+ </Badge>,
+ );
+ expect(screen.getByRole('img')).toHaveAccessibleName('This 23');
});
<SoftwareImpactMeasureCard
branch={branch}
component={component}
+ conditions={conditions}
softwareQuality={SoftwareQuality.Security}
ratingMetricKey={MetricKey.security_rating}
measures={measures}
<SoftwareImpactMeasureCard
branch={branch}
component={component}
+ conditions={conditions}
softwareQuality={SoftwareQuality.Reliability}
ratingMetricKey={MetricKey.reliability_rating}
measures={measures}
<SoftwareImpactMeasureCard
branch={branch}
component={component}
+ conditions={conditions}
softwareQuality={SoftwareQuality.Maintainability}
ratingMetricKey={MetricKey.sqale_rating}
measures={measures}
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 {
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[];
}
export function SoftwareImpactMeasureCard(props: Readonly<SoftwareImpactBreakdownCardProps>) {
- const { component, softwareQuality, ratingMetricKey, measures, branch } = props;
+ const { component, conditions, softwareQuality, ratingMetricKey, measures, branch } = props;
const intl = useIntl();
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">
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();
// 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();
[SoftwareImpactSeverity.Low]: 1,
},
[false, true, false],
+ undefined,
+ true,
);
ui.expectSoftwareImpactMeasureCard(
SoftwareQuality.Maintainability,
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()),
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', () => {
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', () => {
[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();
[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();
[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();
...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', () => {
...(({ [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', () => {
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 () => {
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
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