Ver código fonte

SONAR-21766 Project list uses old measures if there are no new ones

tags/10.5.0.89998
Viktor Vorona 2 meses atrás
pai
commit
e06cde02ac

+ 22
- 6
server/sonar-web/src/main/js/apps/projects/components/project-card/ProjectCard.tsx Ver arquivo

themeBorder, themeBorder,
themeColor, themeColor,
} from 'design-system'; } from 'design-system';
import { isEmpty } from 'lodash';
import * as React from 'react'; import * as React from 'react';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';
import Favorite from '../../../../components/controls/Favorite'; import Favorite from '../../../../components/controls/Favorite';
isNewCode: boolean, isNewCode: boolean,
) { ) {
const { analysisDate, isFavorite, key, measures, name, qualifier, tags, visibility } = project; const { analysisDate, isFavorite, key, measures, name, qualifier, tags, visibility } = project;
const awaitingScan =
[
MetricKey.reliability_issues,
MetricKey.maintainability_issues,
MetricKey.security_issues,
].every((key) => measures[key] === undefined) &&
!isNewCode &&
!isEmpty(analysisDate) &&
measures.ncloc !== undefined;
const formatted = formatMeasure(measures[MetricKey.alert_status], MetricType.Level); const formatted = formatMeasure(measures[MetricKey.alert_status], MetricType.Level);
const qualityGateLabel = translateWithParameters('overview.quality_gate_x', formatted); const qualityGateLabel = translateWithParameters('overview.quality_gate_x', formatted);
return ( return (
<Badge className="sw-ml-2">{translate('visibility', visibility)}</Badge> <Badge className="sw-ml-2">{translate('visibility', visibility)}</Badge>
</span> </span>
</Tooltip> </Tooltip>

{awaitingScan && !isNewCode && !isEmpty(analysisDate) && measures.ncloc !== undefined && (
<Tooltip overlay={translate(`projects.awaiting_scan.description.${qualifier}`)}>
<span>
<Badge variant="new" className="sw-ml-2">
{translate('projects.awaiting_scan')}
</Badge>
</span>
</Tooltip>
)}
</div> </div>


{isDefined(analysisDate) && analysisDate !== '' && ( {isDefined(analysisDate) && analysisDate !== '' && (
) { ) {
const { analysisDate, key, leakPeriodDate, measures, qualifier, isScannable } = project; const { analysisDate, key, leakPeriodDate, measures, qualifier, isScannable } = project;


if (
isDefined(analysisDate) &&
analysisDate !== '' &&
(!isNewCode || (isDefined(leakPeriodDate) && leakPeriodDate !== ''))
) {
if (!isEmpty(analysisDate) && (!isNewCode || !isEmpty(leakPeriodDate))) {
return ( return (
<ProjectCardMeasures <ProjectCardMeasures
measures={measures} measures={measures}
</Note> </Note>


{qualifier !== ComponentQualifier.Application && {qualifier !== ComponentQualifier.Application &&
(analysisDate === undefined || analysisDate === '') &&
isEmpty(analysisDate) &&
isLoggedIn(currentUser) && isLoggedIn(currentUser) &&
isScannable && ( isScannable && (
<Link className="sw-ml-2 sw-body-sm-highlight" to={getProjectUrl(key)}> <Link className="sw-ml-2 sw-body-sm-highlight" to={getProjectUrl(key)}>

+ 12
- 3
server/sonar-web/src/main/js/apps/projects/components/project-card/ProjectCardMeasures.tsx Ver arquivo

{ {
iconLabel: translate(`metric.${MetricKey.security_issues}.short_name`), iconLabel: translate(`metric.${MetricKey.security_issues}.short_name`),
noShrink: true, noShrink: true,
metricKey: MetricKey.security_issues,
metricKey:
measures[MetricKey.security_issues] !== undefined
? MetricKey.security_issues
: MetricKey.vulnerabilities,
metricRatingKey: MetricKey.security_rating, metricRatingKey: MetricKey.security_rating,
metricType: MetricType.ShortInteger, metricType: MetricType.ShortInteger,
}, },
{ {
iconLabel: translate(`metric.${MetricKey.reliability_issues}.short_name`), iconLabel: translate(`metric.${MetricKey.reliability_issues}.short_name`),
metricKey: MetricKey.reliability_issues,
metricKey:
measures[MetricKey.reliability_issues] !== undefined
? MetricKey.reliability_issues
: MetricKey.bugs,
metricRatingKey: MetricKey.reliability_rating, metricRatingKey: MetricKey.reliability_rating,
metricType: MetricType.ShortInteger, metricType: MetricType.ShortInteger,
}, },
{ {
iconLabel: translate(`metric.${MetricKey.maintainability_issues}.short_name`), iconLabel: translate(`metric.${MetricKey.maintainability_issues}.short_name`),
metricKey: MetricKey.maintainability_issues,
metricKey:
measures[MetricKey.maintainability_issues] !== undefined
? MetricKey.maintainability_issues
: MetricKey.code_smells,
metricRatingKey: MetricKey.sqale_rating, metricRatingKey: MetricKey.sqale_rating,
metricType: MetricType.ShortInteger, metricType: MetricType.ShortInteger,
}, },

+ 91
- 4
server/sonar-web/src/main/js/apps/projects/components/project-card/__tests__/ProjectCard-test.tsx Ver arquivo

import { mockCurrentUser, mockLoggedInUser } from '../../../../../helpers/testMocks'; import { mockCurrentUser, mockLoggedInUser } from '../../../../../helpers/testMocks';
import { renderComponent } from '../../../../../helpers/testReactTestingUtils'; import { renderComponent } from '../../../../../helpers/testReactTestingUtils';
import { ComponentQualifier, Visibility } from '../../../../../types/component'; import { ComponentQualifier, Visibility } from '../../../../../types/component';
import { MetricKey } from '../../../../../types/metrics';
import { CurrentUser } from '../../../../../types/users'; import { CurrentUser } from '../../../../../types/users';
import { Project } from '../../../types'; import { Project } from '../../../types';
import ProjectCard from '../ProjectCard'; import ProjectCard from '../ProjectCard';


const MEASURES = { const MEASURES = {
alert_status: 'OK',
reliability_rating: '1.0',
sqale_rating: '1.0',
new_bugs: '12',
[MetricKey.ncloc]: '1000',
[MetricKey.alert_status]: 'OK',
[MetricKey.reliability_rating]: '1.0',
[MetricKey.security_rating]: '1.0',
[MetricKey.sqale_rating]: '1.0',
[MetricKey.new_bugs]: '12',
}; };


const PROJECT: Project = { const PROJECT: Project = {
expect(screen.getByLabelText('qualifier.APP')).toBeInTheDocument(); expect(screen.getByLabelText('qualifier.APP')).toBeInTheDocument();
}); });


it('should not display awaiting analysis badge and do not display old measures', () => {
renderProjectCard({
...PROJECT,
measures: {
...MEASURES,
[MetricKey.security_issues]: JSON.stringify({ LOW: 0, MEDIUM: 0, HIGH: 1, total: 1 }),
[MetricKey.reliability_issues]: JSON.stringify({ LOW: 0, MEDIUM: 2, HIGH: 0, total: 2 }),
[MetricKey.maintainability_issues]: JSON.stringify({ LOW: 3, MEDIUM: 0, HIGH: 0, total: 3 }),
[MetricKey.code_smells]: '4',
[MetricKey.bugs]: '5',
[MetricKey.vulnerabilities]: '6',
},
});
expect(screen.queryByRole('status', { name: 'projects.awaiting_scan' })).not.toBeInTheDocument();
expect(screen.getByText('1')).toBeInTheDocument();
expect(screen.getByText('2')).toBeInTheDocument();
expect(screen.getByText('3')).toBeInTheDocument();
expect(screen.queryByText('4')).not.toBeInTheDocument();
expect(screen.queryByText('5')).not.toBeInTheDocument();
expect(screen.queryByText('6')).not.toBeInTheDocument();
});

it('should display awaiting analysis badge and show the old measures', async () => {
renderProjectCard({
...PROJECT,
measures: {
...MEASURES,
[MetricKey.code_smells]: '4',
[MetricKey.bugs]: '5',
[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('4')).toBeInTheDocument();
expect(screen.getByText('5')).toBeInTheDocument();
expect(screen.getByText('6')).toBeInTheDocument();
});

it('should display awaiting analysis badge and show the old measures for Application', async () => {
renderProjectCard({
...PROJECT,
qualifier: ComponentQualifier.Application,
measures: {
...MEASURES,
[MetricKey.code_smells]: '4',
[MetricKey.bugs]: '5',
[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('4')).toBeInTheDocument();
expect(screen.getByText('5')).toBeInTheDocument();
expect(screen.getByText('6')).toBeInTheDocument();
});

it('should not display awaiting analysis badge if project is not analyzed', () => {
renderProjectCard({
...PROJECT,
analysisDate: undefined,
});
expect(screen.queryByRole('status', { name: 'projects.awaiting_scan' })).not.toBeInTheDocument();
});

it('should not display awaiting analysis badge if project does not have lines of code', () => {
renderProjectCard({
...PROJECT,
measures: {
...(({ [MetricKey.ncloc]: _, ...rest }) => rest)(MEASURES),
},
});
expect(screen.queryByRole('status', { name: '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();
});

it('should display 3 aplication', () => { it('should display 3 aplication', () => {
renderProjectCard({ renderProjectCard({
...PROJECT, ...PROJECT,

+ 3
- 0
server/sonar-web/src/main/js/apps/projects/utils.ts Ver arquivo

export const METRICS = [ export const METRICS = [
MetricKey.alert_status, MetricKey.alert_status,
MetricKey.reliability_issues, MetricKey.reliability_issues,
MetricKey.bugs,
MetricKey.reliability_rating, MetricKey.reliability_rating,
MetricKey.security_issues, MetricKey.security_issues,
MetricKey.vulnerabilities,
MetricKey.security_rating, MetricKey.security_rating,
MetricKey.maintainability_issues, MetricKey.maintainability_issues,
MetricKey.code_smells,
MetricKey.sqale_rating, MetricKey.sqale_rating,
MetricKey.security_hotspots_reviewed, MetricKey.security_hotspots_reviewed,
MetricKey.security_review_rating, MetricKey.security_review_rating,

+ 4
- 0
sonar-core/src/main/resources/org/sonar/l10n/core.properties Ver arquivo

projects.sort.-size=by size (biggest first) projects.sort.-size=by size (biggest first)
projects.show_more=Show more projects projects.show_more=Show more projects
projects.security_hotspots_reviewed=Hotspots Reviewed projects.security_hotspots_reviewed=Hotspots Reviewed
projects.awaiting_scan=Change in Calculation
projects.awaiting_scan.description.TRK=The way Security, Reliability, and Maintainability counts are calculated has changed. The values currently displayed may change after the next analysis.
projects.awaiting_scan.description.APP=The way Security, Reliability, and Maintainability counts are calculated has changed. The values currently displayed may change after all projects in this application have been analyzed.



#------------------------------------------------------------------------------ #------------------------------------------------------------------------------
# #

Carregando…
Cancelar
Salvar