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)}> |
{ | { | ||||
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, | ||||
}, | }, |
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, |
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, |
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. | |||||
#------------------------------------------------------------------------------ | #------------------------------------------------------------------------------ | ||||
# | # |