Browse Source

SONAR-21765 Fix security hotspot link in the new code overview tab

tags/10.5.0.89998
Mathieu Suen 1 month ago
parent
commit
1a0f4685e9

+ 1
- 0
server/sonar-web/src/main/js/apps/overview/branches/NewCodeMeasuresPanel.tsx View File

@@ -172,6 +172,7 @@ export default function NewCodeMeasuresPanel(props: Readonly<Props>) {
: 'issue.type.SECURITY_HOTSPOT.plural'
}
url={getComponentSecurityHotspotsUrl(component.key, {
inNewCodePeriod: 'true',
...getBranchLikeQuery(branch),
})}
value={newSecurityHotspots}

+ 0
- 3
server/sonar-web/src/main/js/apps/overview/branches/OverallCodeMeasuresPanel.tsx View File

@@ -42,7 +42,6 @@ import { QualityGateStatus } from '../../../types/quality-gates';
import { Component, MeasureEnhanced } from '../../../types/types';
import MeasuresCard from '../components/MeasuresCard';
import MeasuresCardNumber from '../components/MeasuresCardNumber';
import { OverviewDisabledLinkTooltip } from '../components/OverviewDisabledLinkTooltip';
import MeasuresPanelPercentCards from './MeasuresPanelPercentCards';
import SoftwareImpactMeasureCard from './SoftwareImpactMeasureCard';

@@ -108,8 +107,6 @@ export default function OverallCodeMeasuresPanel(props: Readonly<OverallCodeMeas
color={acceptedIssues === '0' ? 'overviewCardDefaultIcon' : 'overviewCardWarningIcon'}
/>
}
disabled={component.needIssueSync}
tooltip={component.needIssueSync ? <OverviewDisabledLinkTooltip /> : null}
>
<TextSubdued className="sw-body-xs sw-mt-3">
{intl.formatMessage({

+ 4
- 12
server/sonar-web/src/main/js/apps/overview/branches/SoftwareImpactMeasureCard.tsx View File

@@ -19,7 +19,6 @@
*/
import styled from '@emotion/styled';
import { LinkHighlight, LinkStandalone } from '@sonarsource/echoes-react';
import classNames from 'classnames';
import { Badge, LightGreyCard, LightGreyCardTitle, TextBold, TextSubdued } from 'design-system';
import * as React from 'react';
import { FormattedMessage, useIntl } from 'react-intl';
@@ -41,7 +40,6 @@ import {
import { MetricKey, MetricType } from '../../../types/metrics';
import { QualityGateStatusConditionEnhanced } from '../../../types/quality-gates';
import { Component, MeasureEnhanced } from '../../../types/types';
import { OverviewDisabledLinkTooltip } from '../components/OverviewDisabledLinkTooltip';
import { Status, softwareQualityToMeasure } from '../utils';
import SoftwareImpactMeasureBreakdownCard from './SoftwareImpactMeasureBreakdownCard';
import SoftwareImpactMeasureRating from './SoftwareImpactMeasureRating';
@@ -88,11 +86,9 @@ export function SoftwareImpactMeasureCard(props: Readonly<SoftwareImpactBreakdow
(severity) => measure[severity] > 0,
);

const countTooltipOverlay = component.needIssueSync ? (
<OverviewDisabledLinkTooltip />
) : (
intl.formatMessage({ id: 'overview.measures.software_impact.count_tooltip' })
);
const countTooltipOverlay = intl.formatMessage({
id: 'overview.measures.software_impact.count_tooltip',
});

const failed = conditions.some((c) => c.level === Status.ERROR && c.metric === ratingMetricKey);

@@ -111,11 +107,7 @@ export function SoftwareImpactMeasureCard(props: Readonly<SoftwareImpactBreakdow
</LightGreyCardTitle>
<div className="sw-flex sw-flex-col sw-gap-3">
<div className="sw-flex sw-mt-4">
<div
className={classNames('sw-flex sw-gap-1 sw-items-center', {
'sw-opacity-60': component.needIssueSync,
})}
>
<div className="sw-flex sw-gap-1 sw-items-center">
{count ? (
<Tooltip overlay={countTooltipOverlay}>
<LinkStandalone

+ 5
- 33
server/sonar-web/src/main/js/apps/overview/branches/__tests__/BranchOverview-it.tsx View File

@@ -250,6 +250,11 @@ describe('project overview', () => {
expect(await screen.findByText('metric.level.ERROR')).toBeInTheDocument();
expect(screen.getByText(/overview.X_conditions_failed/)).toBeInTheDocument();
expect(screen.getAllByText(/overview.quality_gate.required_x/)).toHaveLength(3);
expect(
screen.getByRole('link', {
name: '1 1 metric.new_security_hotspots_reviewed.name quality_gates.operator.GT 2',
}),
).toHaveAttribute('href', '/security_hotspots?id=foo&inNewCodePeriod=true');
});

it('should correctly show a project as empty', async () => {
@@ -437,39 +442,6 @@ describe('project overview', () => {
expect(await screen.findByText('overview.missing_project_data.TRK')).toBeInTheDocument();
},
);

it('should disable software impact measure card links during reindexing', async () => {
const { user, ui } = getPageObjects();
renderBranchOverview({
component: mockComponent({
breadcrumbs: [mockComponent({ key: 'foo' })],
key: 'foo',
needIssueSync: true,
}),
});

await user.click(await ui.overallCodeButton.find());

expect(await ui.softwareImpactMeasureCard(SoftwareQuality.Security).find()).toBeInTheDocument();

ui.expectSoftwareImpactMeasureCard(
SoftwareQuality.Security,
'B',
{
total: 1,
[SoftwareImpactSeverity.High]: 0,
[SoftwareImpactSeverity.Medium]: 1,
[SoftwareImpactSeverity.Low]: 0,
},
[false, true, false],
);

await expect(
byRole('link', {
name: `overview.measures.software_impact.see_list_of_x_open_issues.${1}.software_quality.${SoftwareQuality.Security}`,
}).get(),
).toHaveATooltipWithContent('indexation.in_progress');
});
});

describe('application overview', () => {

+ 17
- 23
server/sonar-web/src/main/js/apps/overview/components/IssueMeasuresCardInner.tsx View File

@@ -18,15 +18,14 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import styled from '@emotion/styled';
import { LinkHighlight, LinkStandalone } from '@sonarsource/echoes-react';
import classNames from 'classnames';
import { Badge, ContentLink, NoDataIcon, themeColor } from 'design-system';
import { Badge, NoDataIcon, themeColor } from 'design-system';
import * as React from 'react';
import { Path } from 'react-router-dom';
import Tooltip from '../../../components/controls/Tooltip';
import { translate, translateWithParameters } from '../../../helpers/l10n';
import { localizeMetric } from '../../../helpers/measures';
import { MetricKey } from '../../../types/metrics';
import { OverviewDisabledLinkTooltip } from './OverviewDisabledLinkTooltip';

interface IssueMeasuresCardInnerProps extends React.HTMLAttributes<HTMLDivElement> {
metric: MetricKey;
@@ -60,27 +59,22 @@ export function IssueMeasuresCardInner(props: Readonly<IssueMeasuresCardInnerPro
</ColorBold>
<div className="sw-flex sw-justify-between sw-items-center sw-h-9">
<div className="sw-h-fit">
<Tooltip
classNameSpace={disabled ? 'tooltip' : 'sw-hidden'}
overlay={value && <OverviewDisabledLinkTooltip />}
<LinkStandalone
highlight={LinkHighlight.Default}
aria-label={
value
? translateWithParameters(
'overview.see_more_details_on_x_of_y',
value,
localizeMetric(metric),
)
: translateWithParameters('no_measure_value_x', localizeMetric(metric))
}
className="it__overview-measures-value sw-w-fit sw-text-lg"
to={url}
>
<ContentLink
disabled={disabled || !value}
aria-label={
value
? translateWithParameters(
'overview.see_more_details_on_x_of_y',
value,
localizeMetric(metric),
)
: translate('no_data')
}
className="it__overview-measures-value sw-w-fit sw-text-lg"
to={url}
>
{value ? value : '-'}
</ContentLink>
</Tooltip>
{value ?? '-'}
</LinkStandalone>
</div>
{value ? icon : <NoDataIcon size="md" />}
</div>

+ 20
- 22
server/sonar-web/src/main/js/apps/overview/components/MeasuresCard.tsx View File

@@ -18,7 +18,8 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import styled from '@emotion/styled';
import { Badge, Card, ContentLink, Tooltip, themeBorder, themeColor } from 'design-system';
import { LinkHighlight, LinkStandalone } from '@sonarsource/echoes-react';
import { Badge, Card, themeBorder, themeColor } from 'design-system';
import * as React from 'react';
import { To } from 'react-router-dom';
import { translate, translateWithParameters } from '../../../helpers/l10n';
@@ -32,14 +33,12 @@ export interface MeasuresCardProps {
label: string;
failed?: boolean;
icon?: React.ReactElement;
disabled?: boolean;
tooltip?: React.ReactNode | null;
}

export default function MeasuresCard(
props: React.PropsWithChildren<MeasuresCardProps & React.HTMLAttributes<HTMLDivElement>>,
) {
const { failed, children, metric, icon, value, url, label, disabled, tooltip, ...rest } = props;
const { failed, children, metric, icon, value, url, label, ...rest } = props;

return (
<StyledCard className="sw-p-6 sw-rounded-2 sw-text-base" {...rest}>
@@ -50,24 +49,23 @@ export default function MeasuresCard(
</Badge>
)}
<div className="sw-flex sw-items-center sw-mt-1 sw-justify-between sw-font-semibold">
{value ? (
<Tooltip overlay={tooltip}>
<ContentLink
aria-label={translateWithParameters(
'overview.see_more_details_on_x_of_y',
value,
localizeMetric(metric),
)}
className="it__overview-measures-value sw-text-lg"
to={url}
disabled={disabled}
>
{value}
</ContentLink>
</Tooltip>
) : (
<ColorBold> — </ColorBold>
)}
<LinkStandalone
highlight={LinkHighlight.Default}
aria-label={
value
? translateWithParameters(
'overview.see_more_details_on_x_of_y',
value,
localizeMetric(metric),
)
: translateWithParameters('no_measure_value_x', localizeMetric(metric))
}
className="it__overview-measures-value sw-text-lg"
to={url}
>
{value ?? '-'}
</LinkStandalone>

{icon}
</div>
{children && <div className="sw-flex sw-flex-col">{children}</div>}

+ 0
- 42
server/sonar-web/src/main/js/apps/overview/components/OverviewDisabledLinkTooltip.tsx View File

@@ -1,42 +0,0 @@
/*
* SonarQube
* Copyright (C) 2009-2024 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
import DocumentationLink from '../../../components/common/DocumentationLink';
import { translate } from '../../../helpers/l10n';

export function OverviewDisabledLinkTooltip() {
return (
<div className="sw-body-sm sw-w-[280px]">
{translate('indexation.in_progress')}

<br />

{translate('indexation.link_unavailable')}

<hr className="sw-mx-0 sw-my-3 sw-p-0 sw-w-full" />

<span className="sw-body-sm-highlight">{translate('indexation.learn_more')}</span>

<DocumentationLink className="sw-ml-1" to="/instance-administration/reindexing/">
{translate('indexation.reindexing')}
</DocumentationLink>
</div>
);
}

+ 11
- 1
server/sonar-web/src/main/js/apps/overview/pullRequests/__tests__/PullRequestOverview-it.tsx View File

@@ -155,7 +155,17 @@ it('should render links correctly', async () => {
}).get(),
).toHaveAttribute('href', '/project/issues?fixedInPullRequest=1001&id=foo');

expect(screen.getByLabelText('no_data')).toBeInTheDocument();
expect(
screen.getByRole('link', { name: 'no_measure_value_x.metric.new_security_hotspots.name' }),
).toBeInTheDocument();
expect(
screen.getByRole('link', { name: 'no_measure_value_x.metric.new_accepted_issues.name' }),
).toBeInTheDocument();
expect(
screen.getByRole('link', {
name: 'no_measure_value_x.metric.new_duplicated_lines_density.name',
}),
).toBeInTheDocument();
});

it('should render correctly for a passed QG', async () => {

+ 1
- 1
sonar-core/src/main/resources/org/sonar/l10n/core.properties View File

@@ -310,6 +310,7 @@ more_information=More information
new_violations=New violations
new_window=New window
no_data=No data
no_measure_value_x=No measure value for {0}
no_results=No results
no_results_for_x=No results for "{0}"
no_results_search=We couldn't find any results matching selected criteria.
@@ -5165,7 +5166,6 @@ maintenance.sonarqube_is_offline.text=The connection to SonarQube is lost. Pleas
#------------------------------------------------------------------------------
indexation.in_progress=Reindexing in progress.
indexation.details_unavailable=Details are unavailable until this process is complete.
indexation.link_unavailable=The link to these results is unavailable until this process is complete.
indexation.features_partly_available=Most features are available. Some details only show upon completion. {link}
indexation.features_partly_available.link=More info
indexation.progression={0} out of {1} projects reindexed.

Loading…
Cancel
Save