aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMathieu Suen <mathieu.suen@sonarsource.com>2020-01-31 15:15:36 +0100
committerSonarTech <sonartech@sonarsource.com>2020-02-11 20:46:11 +0100
commit0a1d327928f35e177f3190d810105eb7e661ebc7 (patch)
tree6ab73d722ed8f77542603a390f4e7f0dc18bf8c8
parentec64094599252553e55efa348516ff36477bce57 (diff)
downloadsonarqube-0a1d327928f35e177f3190d810105eb7e661ebc7.tar.gz
sonarqube-0a1d327928f35e177f3190d810105eb7e661ebc7.zip
SONAR-12981 Add security review rating and hotspots in PR overview.
-rw-r--r--server/sonar-web/src/main/js/apps/overview/components/IssueLabel.tsx8
-rw-r--r--server/sonar-web/src/main/js/apps/overview/components/IssueRating.tsx42
-rw-r--r--server/sonar-web/src/main/js/apps/overview/components/__tests__/IssueRating-test.tsx4
-rw-r--r--server/sonar-web/src/main/js/apps/overview/components/__tests__/__snapshots__/IssueLabel-test.tsx.snap10
-rw-r--r--server/sonar-web/src/main/js/apps/overview/components/__tests__/__snapshots__/IssueRating-test.tsx.snap15
-rw-r--r--server/sonar-web/src/main/js/apps/overview/pullRequests/PullRequestOverview.tsx63
-rw-r--r--server/sonar-web/src/main/js/apps/overview/pullRequests/__tests__/__snapshots__/PullRequestOverview-test.tsx.snap246
-rw-r--r--server/sonar-web/src/main/js/apps/overview/utils.ts8
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/ProjectCardLeakMeasures.tsx103
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/ProjectCardOverallMeasures.tsx104
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/ProjectCardRatingMeasure.tsx59
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCardRatingMeasure-test.tsx47
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardLeakMeasures-test.tsx.snap384
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardOverallMeasures-test.tsx.snap198
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardRatingMeasure-test.tsx.snap69
-rw-r--r--server/sonar-web/src/main/js/helpers/urls.ts4
-rw-r--r--sonar-core/src/main/resources/org/sonar/l10n/core.properties6
17 files changed, 797 insertions, 573 deletions
diff --git a/server/sonar-web/src/main/js/apps/overview/components/IssueLabel.tsx b/server/sonar-web/src/main/js/apps/overview/components/IssueLabel.tsx
index 1460880bd9e..3a55142b090 100644
--- a/server/sonar-web/src/main/js/apps/overview/components/IssueLabel.tsx
+++ b/server/sonar-web/src/main/js/apps/overview/components/IssueLabel.tsx
@@ -25,7 +25,7 @@ import DocTooltip from '../../../components/docs/DocTooltip';
import { getLeakValue } from '../../../components/measure/utils';
import { getBranchLikeQuery } from '../../../helpers/branch-like';
import { findMeasure } from '../../../helpers/measures';
-import { getComponentIssuesUrl } from '../../../helpers/urls';
+import { getComponentIssuesUrl, getComponentSecurityHotspotsUrl } from '../../../helpers/urls';
import { BranchLike } from '../../../types/branch-like';
import { getIssueIconClass, getIssueMetricKey, IssueType } from '../utils';
@@ -63,7 +63,11 @@ export function IssueLabel(props: IssueLabelProps) {
) : (
<Link
className="overview-measures-value text-light"
- to={getComponentIssuesUrl(component.key, params)}>
+ to={
+ type === IssueType.SecurityHotspot
+ ? getComponentSecurityHotspotsUrl(component.key, getBranchLikeQuery(branchLike))
+ : getComponentIssuesUrl(component.key, params)
+ }>
{formatMeasure(value, 'SHORT_INT')}
</Link>
)}
diff --git a/server/sonar-web/src/main/js/apps/overview/components/IssueRating.tsx b/server/sonar-web/src/main/js/apps/overview/components/IssueRating.tsx
index 9379a4078e3..05279460952 100644
--- a/server/sonar-web/src/main/js/apps/overview/components/IssueRating.tsx
+++ b/server/sonar-web/src/main/js/apps/overview/components/IssueRating.tsx
@@ -34,32 +34,44 @@ export interface IssueRatingProps {
useDiffMetric?: boolean;
}
-export function IssueRating(props: IssueRatingProps) {
- const { branchLike, component, measures, type, useDiffMetric = false } = props;
+function renderRatingLink(props: IssueRatingProps) {
+ const { branchLike, component, useDiffMetric = false, measures, type } = props;
const rating = getIssueRatingMetricKey(type, useDiffMetric);
const measure = findMeasure(measures, rating);
if (!rating || !measure) {
- return null;
+ return (
+ <div className="padded">
+ <Rating value={undefined} />
+ </div>
+ );
}
- const value = useDiffMetric ? getLeakValue(measure) : measure.value;
+ const value = measure && (useDiffMetric ? getLeakValue(measure) : measure.value);
const tooltip = value && getRatingTooltip(rating, Number(value));
return (
+ <Tooltip overlay={tooltip}>
+ <span>
+ <DrilldownLink
+ branchLike={branchLike}
+ className="link-no-underline"
+ component={component.key}
+ metric={rating}>
+ <Rating value={value} />
+ </DrilldownLink>
+ </span>
+ </Tooltip>
+ );
+}
+
+export function IssueRating(props: IssueRatingProps) {
+ const { type } = props;
+
+ return (
<>
<span className="flex-1 big-spacer-right text-right">{getIssueRatingName(type)}</span>
- <Tooltip overlay={tooltip}>
- <span>
- <DrilldownLink
- branchLike={branchLike}
- className="link-no-underline"
- component={component.key}
- metric={rating}>
- <Rating value={value} />
- </DrilldownLink>
- </span>
- </Tooltip>
+ {renderRatingLink(props)}
</>
);
}
diff --git a/server/sonar-web/src/main/js/apps/overview/components/__tests__/IssueRating-test.tsx b/server/sonar-web/src/main/js/apps/overview/components/__tests__/IssueRating-test.tsx
index a4647657af6..1b660f3c124 100644
--- a/server/sonar-web/src/main/js/apps/overview/components/__tests__/IssueRating-test.tsx
+++ b/server/sonar-web/src/main/js/apps/overview/components/__tests__/IssueRating-test.tsx
@@ -44,8 +44,8 @@ it('should render correctly if no values are present', () => {
expect(
shallowRender({
measures: [mockMeasureEnhanced({ metric: mockMetric({ key: 'NONE' }) })]
- }).type()
- ).toBeNull();
+ })
+ ).toMatchSnapshot();
});
function shallowRender(props: Partial<IssueRatingProps> = {}) {
diff --git a/server/sonar-web/src/main/js/apps/overview/components/__tests__/__snapshots__/IssueLabel-test.tsx.snap b/server/sonar-web/src/main/js/apps/overview/components/__tests__/__snapshots__/IssueLabel-test.tsx.snap
index 04a4cf74349..4759ea75392 100644
--- a/server/sonar-web/src/main/js/apps/overview/components/__tests__/__snapshots__/IssueLabel-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/overview/components/__tests__/__snapshots__/IssueLabel-test.tsx.snap
@@ -120,13 +120,10 @@ exports[`should render correctly for hotspots 1`] = `
style={Object {}}
to={
Object {
- "pathname": "/project/issues",
+ "pathname": "/security_hotspots",
"query": Object {
"id": "my-project",
"pullRequest": "1001",
- "resolved": "false",
- "sinceLeakPeriod": "false",
- "types": "SECURITY_HOTSPOT",
},
}
}
@@ -152,13 +149,10 @@ exports[`should render correctly for hotspots 2`] = `
style={Object {}}
to={
Object {
- "pathname": "/project/issues",
+ "pathname": "/security_hotspots",
"query": Object {
"id": "my-project",
"pullRequest": "1001",
- "resolved": "false",
- "sinceLeakPeriod": "true",
- "types": "SECURITY_HOTSPOT",
},
}
}
diff --git a/server/sonar-web/src/main/js/apps/overview/components/__tests__/__snapshots__/IssueRating-test.tsx.snap b/server/sonar-web/src/main/js/apps/overview/components/__tests__/__snapshots__/IssueRating-test.tsx.snap
index 45be5c5c55a..f8f24334651 100644
--- a/server/sonar-web/src/main/js/apps/overview/components/__tests__/__snapshots__/IssueRating-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/overview/components/__tests__/__snapshots__/IssueRating-test.tsx.snap
@@ -209,3 +209,18 @@ exports[`should render correctly for vulnerabilities 2`] = `
</Tooltip>
</Fragment>
`;
+
+exports[`should render correctly if no values are present 1`] = `
+<Fragment>
+ <span
+ className="flex-1 big-spacer-right text-right"
+ >
+ metric_domain.Reliability
+ </span>
+ <div
+ className="padded"
+ >
+ <Rating />
+ </div>
+</Fragment>
+`;
diff --git a/server/sonar-web/src/main/js/apps/overview/pullRequests/PullRequestOverview.tsx b/server/sonar-web/src/main/js/apps/overview/pullRequests/PullRequestOverview.tsx
index 38c6b0b3fee..eeec9f5ea7d 100644
--- a/server/sonar-web/src/main/js/apps/overview/pullRequests/PullRequestOverview.tsx
+++ b/server/sonar-web/src/main/js/apps/overview/pullRequests/PullRequestOverview.tsx
@@ -175,44 +175,33 @@ export class PullRequestOverview extends React.PureComponent<Props, State> {
</h2>
<div className="overview-panel-content">
- {[IssueType.Bug, IssueType.Vulnerability, IssueType.CodeSmell].map(
- (type: IssueType) => (
- <div className="overview-measures-row display-flex-row" key={type}>
- <div className="overview-panel-big-padded flex-1 small display-flex-center">
- <IssueLabel
- branchLike={branchLike}
- component={component}
- measures={measures}
- type={type}
- useDiffMetric={true}
- />
- </div>
- {type === 'VULNERABILITY' && (
- <div className="flex-1 small display-flex-center">
- <IssueLabel
- branchLike={branchLike}
- component={component}
- docTooltip={import(
- /* webpackMode: "eager" */ 'Docs/tooltips/metrics/security-hotspots.md'
- )}
- measures={measures}
- type={IssueType.SecurityHotspot}
- useDiffMetric={true}
- />
- </div>
- )}
- <div className="overview-panel-big-padded overview-measures-aside display-flex-center">
- <IssueRating
- branchLike={branchLike}
- component={component}
- measures={measures}
- type={type}
- useDiffMetric={true}
- />
- </div>
+ {[
+ IssueType.Bug,
+ IssueType.Vulnerability,
+ IssueType.SecurityHotspot,
+ IssueType.CodeSmell
+ ].map((type: IssueType) => (
+ <div className="overview-measures-row display-flex-row" key={type}>
+ <div className="overview-panel-big-padded flex-1 small display-flex-center">
+ <IssueLabel
+ branchLike={branchLike}
+ component={component}
+ measures={measures}
+ type={type}
+ useDiffMetric={true}
+ />
</div>
- )
- )}
+ <div className="overview-panel-big-padded overview-measures-aside display-flex-center">
+ <IssueRating
+ branchLike={branchLike}
+ component={component}
+ measures={measures}
+ type={type}
+ useDiffMetric={true}
+ />
+ </div>
+ </div>
+ ))}
{[MeasurementType.Coverage, MeasurementType.Duplication].map(
(type: MeasurementType) => (
diff --git a/server/sonar-web/src/main/js/apps/overview/pullRequests/__tests__/__snapshots__/PullRequestOverview-test.tsx.snap b/server/sonar-web/src/main/js/apps/overview/pullRequests/__tests__/__snapshots__/PullRequestOverview-test.tsx.snap
index 3d778f26731..267d6b7bd55 100644
--- a/server/sonar-web/src/main/js/apps/overview/pullRequests/__tests__/__snapshots__/PullRequestOverview-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/overview/pullRequests/__tests__/__snapshots__/PullRequestOverview-test.tsx.snap
@@ -483,7 +483,125 @@ exports[`should render correctly for a failed QG 1`] = `
/>
</div>
<div
- className="flex-1 small display-flex-center"
+ className="overview-panel-big-padded overview-measures-aside display-flex-center"
+ >
+ <Memo(IssueRating)
+ branchLike={
+ Object {
+ "analysisDate": "2018-01-01",
+ "base": "master",
+ "branch": "feature/foo/bar",
+ "key": "1001",
+ "target": "master",
+ "title": "Foo Bar feature",
+ }
+ }
+ component={
+ Object {
+ "breadcrumbs": Array [],
+ "key": "my-project",
+ "name": "MyProject",
+ "organization": "foo",
+ "qualifier": "TRK",
+ "qualityGate": Object {
+ "isDefault": true,
+ "key": "30",
+ "name": "Sonar way",
+ },
+ "qualityProfiles": Array [
+ Object {
+ "deleted": false,
+ "key": "my-qp",
+ "language": "ts",
+ "name": "Sonar way",
+ },
+ ],
+ "tags": Array [],
+ }
+ }
+ measures={
+ Array [
+ Object {
+ "bestValue": true,
+ "metric": Object {
+ "id": "new_bugs",
+ "key": "new_bugs",
+ "name": "new_bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "metric": Object {
+ "id": "new_vulnerabilities",
+ "key": "new_vulnerabilities",
+ "name": "new_vulnerabilities",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "metric": Object {
+ "id": "new_code_smells",
+ "key": "new_code_smells",
+ "name": "new_code_smells",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "metric": Object {
+ "id": "new_security_hotspots",
+ "key": "new_security_hotspots",
+ "name": "new_security_hotspots",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ ]
+ }
+ type="VULNERABILITY"
+ useDiffMetric={true}
+ />
+ </div>
+ </div>
+ <div
+ className="overview-measures-row display-flex-row"
+ key="SECURITY_HOTSPOT"
+ >
+ <div
+ className="overview-panel-big-padded flex-1 small display-flex-center"
>
<Memo(IssueLabel)
branchLike={
@@ -519,7 +637,6 @@ exports[`should render correctly for a failed QG 1`] = `
"tags": Array [],
}
}
- docTooltip={Promise {}}
measures={
Array [
Object {
@@ -705,7 +822,7 @@ exports[`should render correctly for a failed QG 1`] = `
},
]
}
- type="VULNERABILITY"
+ type="SECURITY_HOTSPOT"
useDiffMetric={true}
/>
</div>
@@ -1745,7 +1862,125 @@ exports[`should render correctly for a passed QG 1`] = `
/>
</div>
<div
- className="flex-1 small display-flex-center"
+ className="overview-panel-big-padded overview-measures-aside display-flex-center"
+ >
+ <Memo(IssueRating)
+ branchLike={
+ Object {
+ "analysisDate": "2018-01-01",
+ "base": "master",
+ "branch": "feature/foo/bar",
+ "key": "1001",
+ "target": "master",
+ "title": "Foo Bar feature",
+ }
+ }
+ component={
+ Object {
+ "breadcrumbs": Array [],
+ "key": "my-project",
+ "name": "MyProject",
+ "organization": "foo",
+ "qualifier": "TRK",
+ "qualityGate": Object {
+ "isDefault": true,
+ "key": "30",
+ "name": "Sonar way",
+ },
+ "qualityProfiles": Array [
+ Object {
+ "deleted": false,
+ "key": "my-qp",
+ "language": "ts",
+ "name": "Sonar way",
+ },
+ ],
+ "tags": Array [],
+ }
+ }
+ measures={
+ Array [
+ Object {
+ "bestValue": true,
+ "metric": Object {
+ "id": "new_bugs",
+ "key": "new_bugs",
+ "name": "new_bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "metric": Object {
+ "id": "new_vulnerabilities",
+ "key": "new_vulnerabilities",
+ "name": "new_vulnerabilities",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "metric": Object {
+ "id": "new_code_smells",
+ "key": "new_code_smells",
+ "name": "new_code_smells",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "metric": Object {
+ "id": "new_security_hotspots",
+ "key": "new_security_hotspots",
+ "name": "new_security_hotspots",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ ]
+ }
+ type="VULNERABILITY"
+ useDiffMetric={true}
+ />
+ </div>
+ </div>
+ <div
+ className="overview-measures-row display-flex-row"
+ key="SECURITY_HOTSPOT"
+ >
+ <div
+ className="overview-panel-big-padded flex-1 small display-flex-center"
>
<Memo(IssueLabel)
branchLike={
@@ -1781,7 +2016,6 @@ exports[`should render correctly for a passed QG 1`] = `
"tags": Array [],
}
}
- docTooltip={Promise {}}
measures={
Array [
Object {
@@ -1967,7 +2201,7 @@ exports[`should render correctly for a passed QG 1`] = `
},
]
}
- type="VULNERABILITY"
+ type="SECURITY_HOTSPOT"
useDiffMetric={true}
/>
</div>
diff --git a/server/sonar-web/src/main/js/apps/overview/utils.ts b/server/sonar-web/src/main/js/apps/overview/utils.ts
index fa7c0341f9c..85028d81263 100644
--- a/server/sonar-web/src/main/js/apps/overview/utils.ts
+++ b/server/sonar-web/src/main/js/apps/overview/utils.ts
@@ -88,6 +88,7 @@ export const PR_METRICS: string[] = [
MetricKey.new_reliability_rating,
MetricKey.new_vulnerabilities,
MetricKey.new_security_hotspots,
+ MetricKey.new_security_review_rating,
MetricKey.new_security_rating
];
@@ -165,9 +166,10 @@ const ISSUETYPE_MAP = {
[IssueType.SecurityHotspot]: {
metric: MetricKey.security_hotspots,
newMetric: MetricKey.new_security_hotspots,
- rating: '',
- newRating: '',
- ratingName: '',
+ rating: MetricKey.security_review_rating,
+ newRating: MetricKey.new_security_review_rating,
+ ratingName: 'SecurityReview',
+
iconClass: SecurityHotspotIcon
}
};
diff --git a/server/sonar-web/src/main/js/apps/projects/components/ProjectCardLeakMeasures.tsx b/server/sonar-web/src/main/js/apps/projects/components/ProjectCardLeakMeasures.tsx
index f7b873d0808..8357df3a7c2 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/ProjectCardLeakMeasures.tsx
+++ b/server/sonar-web/src/main/js/apps/projects/components/ProjectCardLeakMeasures.tsx
@@ -18,13 +18,9 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
-import BugIcon from 'sonar-ui-common/components/icons/BugIcon';
-import CodeSmellIcon from 'sonar-ui-common/components/icons/CodeSmellIcon';
-import SecurityHotspotIcon from 'sonar-ui-common/components/icons/SecurityHotspotIcon';
-import VulnerabilityIcon from 'sonar-ui-common/components/icons/VulnerabilityIcon';
-import Rating from 'sonar-ui-common/components/ui/Rating';
import { translate } from 'sonar-ui-common/helpers/l10n';
import Measure from '../../../components/measure/Measure';
+import ProjectCardRatingMeasure from './ProjectCardRatingMeasure';
interface Props {
measures: T.Dict<string>;
@@ -33,77 +29,38 @@ interface Props {
export default function ProjectCardLeakMeasures({ measures }: Props) {
return (
<div className="project-card-leak-measures">
- <div className="project-card-measure" data-key="new_reliability_rating">
- <div className="project-card-measure-inner">
- <div className="project-card-measure-number">
- <Measure
- className="spacer-right"
- metricKey="new_bugs"
- metricType="SHORT_INT"
- value={measures['new_bugs']}
- />
- <Rating value={measures['new_reliability_rating']} />
- </div>
- <div className="project-card-measure-label-with-icon">
- <BugIcon className="little-spacer-right text-bottom" />
- {translate('metric.bugs.name')}
- </div>
- </div>
- </div>
+ <ProjectCardRatingMeasure
+ iconLabel={translate('metric.bugs.name')}
+ measures={measures}
+ metricKey="new_bugs"
+ metricRatingKey="new_reliability_rating"
+ metricType="SHORT_INT"
+ />
- <div className="project-card-measure" data-key="new_security_rating">
- <div className="project-card-measure-inner">
- <div className="project-card-measure-number">
- <Measure
- className="spacer-right"
- metricKey="new_vulnerabilities"
- metricType="SHORT_INT"
- value={measures['new_vulnerabilities']}
- />
- <Rating value={measures['new_security_rating']} />
- </div>
- <div className="project-card-measure-label-with-icon">
- <VulnerabilityIcon className="little-spacer-right text-bottom" />
- {translate('metric.vulnerabilities.name')}
- </div>
- </div>
- </div>
+ <ProjectCardRatingMeasure
+ iconLabel={translate('metric.vulnerabilities.name')}
+ measures={measures}
+ metricKey="new_vulnerabilities"
+ metricRatingKey="new_security_rating"
+ metricType="SHORT_INT"
+ />
- <div className="project-card-measure" data-key="new_security_review_rating">
- <div className="project-card-measure-inner">
- <div className="project-card-measure-number">
- <Measure
- className="spacer-right"
- metricKey="new_security_hotspots_reviewed"
- metricType="PERCENT"
- value={measures['new_security_hotspots_reviewed']}
- />
- <Rating value={measures['new_security_review_rating']} />
- </div>
- <div className="project-card-measure-label-with-icon">
- <SecurityHotspotIcon className="little-spacer-right text-bottom" />
- {translate('metric.security_hotspots_reviewed.extra_short_name')}
- </div>
- </div>
- </div>
+ <ProjectCardRatingMeasure
+ iconKey="security_hotspots"
+ iconLabel={translate('projects.security_hotspots_reviewed')}
+ measures={measures}
+ metricKey="new_security_hotspots_reviewed"
+ metricRatingKey="new_security_review_rating"
+ metricType="PERCENT"
+ />
- <div className="project-card-measure" data-key="new_maintainability_rating">
- <div className="project-card-measure-inner">
- <div className="project-card-measure-number">
- <Measure
- className="spacer-right"
- metricKey="new_code_smells"
- metricType="SHORT_INT"
- value={measures['new_code_smells']}
- />
- <Rating value={measures['new_maintainability_rating']} />
- </div>
- <div className="project-card-measure-label-with-icon">
- <CodeSmellIcon className="little-spacer-right text-bottom" />
- {translate('metric.code_smells.name')}
- </div>
- </div>
- </div>
+ <ProjectCardRatingMeasure
+ iconLabel={translate('metric.code_smells.name')}
+ measures={measures}
+ metricKey="new_code_smells"
+ metricRatingKey="new_maintainability_rating"
+ metricType="SHORT_INT"
+ />
{measures['new_coverage'] != null && (
<div className="project-card-measure" data-key="new_coverage">
diff --git a/server/sonar-web/src/main/js/apps/projects/components/ProjectCardOverallMeasures.tsx b/server/sonar-web/src/main/js/apps/projects/components/ProjectCardOverallMeasures.tsx
index 6fd4a355c78..9198fd8f42c 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/ProjectCardOverallMeasures.tsx
+++ b/server/sonar-web/src/main/js/apps/projects/components/ProjectCardOverallMeasures.tsx
@@ -18,17 +18,13 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
-import BugIcon from 'sonar-ui-common/components/icons/BugIcon';
-import CodeSmellIcon from 'sonar-ui-common/components/icons/CodeSmellIcon';
-import SecurityHotspotIcon from 'sonar-ui-common/components/icons/SecurityHotspotIcon';
-import VulnerabilityIcon from 'sonar-ui-common/components/icons/VulnerabilityIcon';
import DuplicationsRating from 'sonar-ui-common/components/ui/DuplicationsRating';
-import Rating from 'sonar-ui-common/components/ui/Rating';
import SizeRating from 'sonar-ui-common/components/ui/SizeRating';
import { translate } from 'sonar-ui-common/helpers/l10n';
import Measure from '../../../components/measure/Measure';
import CoverageRating from '../../../components/ui/CoverageRating';
import ProjectCardLanguagesContainer from './ProjectCardLanguagesContainer';
+import ProjectCardRatingMeasure from './ProjectCardRatingMeasure';
interface Props {
measures: T.Dict<string | undefined>;
@@ -46,77 +42,39 @@ export default function ProjectCardOverallMeasures({ measures }: Props) {
return (
<div className="project-card-measures">
- <div className="project-card-measure" data-key="reliability_rating">
- <div className="project-card-measure-inner">
- <div className="project-card-measure-number">
- <Measure
- className="spacer-right"
- metricKey="bugs"
- metricType="SHORT_INT"
- value={measures['bugs']}
- />
- <Rating value={measures['reliability_rating']} />
- </div>
- <div className="project-card-measure-label-with-icon">
- <BugIcon className="little-spacer-right text-bottom" />
- {translate('metric.bugs.name')}
- </div>
- </div>
- </div>
+ <ProjectCardRatingMeasure
+ iconLabel={translate('metric.bugs.name')}
+ measures={measures}
+ metricKey="bugs"
+ metricRatingKey="reliability_rating"
+ metricType="SHORT_INT"
+ />
- <div className="project-card-measure" data-key="security_rating">
- <div className="project-card-measure-inner">
- <div className="project-card-measure-number">
- <Measure
- className="spacer-right"
- metricKey="vulnerabilities"
- metricType="SHORT_INT"
- value={measures['vulnerabilities']}
- />
- <Rating value={measures['security_rating']} />
- </div>
- <div className="project-card-measure-label-with-icon">
- <VulnerabilityIcon className="little-spacer-right text-bottom" />
- {translate('metric.vulnerabilities.name')}
- </div>
- </div>
- </div>
+ <ProjectCardRatingMeasure
+ iconLabel={translate('metric.vulnerabilities.name')}
+ measures={measures}
+ metricKey="vulnerabilities"
+ metricRatingKey="security_rating"
+ metricType="SHORT_INT"
+ />
- <div className="project-card-measure" data-key="security_review_rating">
- <div className="project-card-measure-inner">
- <div className="project-card-measure-number">
- <Measure
- className="spacer-right"
- metricKey="security_hotspots_reviewed"
- metricType="PERCENT"
- value={measures['security_hotspots_reviewed']}
- />
- <Rating value={measures['security_review_rating']} />
- </div>
- <div className="project-card-measure-label-with-icon">
- <SecurityHotspotIcon className="little-spacer-right text-bottom" />
- {translate('metric.security_hotspots_reviewed.extra_short_name')}
- </div>
- </div>
- </div>
+ <ProjectCardRatingMeasure
+ iconKey="security_hotspots"
+ iconLabel={translate('projects.security_hotspots_reviewed')}
+ measures={measures}
+ metricKey="security_hotspots_reviewed"
+ metricRatingKey="security_review_rating"
+ metricType="PERCENT"
+ />
+
+ <ProjectCardRatingMeasure
+ iconLabel={translate('metric.code_smells.name')}
+ measures={measures}
+ metricKey="code_smells"
+ metricRatingKey="sqale_rating"
+ metricType="SHORT_INT"
+ />
- <div className="project-card-measure" data-key="sqale_rating">
- <div className="project-card-measure-inner">
- <div className="project-card-measure-number">
- <Measure
- className="spacer-right"
- metricKey="code_smells"
- metricType="SHORT_INT"
- value={measures['code_smells']}
- />
- <Rating value={measures['sqale_rating']} />
- </div>
- <div className="project-card-measure-label-with-icon">
- <CodeSmellIcon className="little-spacer-right text-bottom" />
- {translate('metric.code_smells.name')}
- </div>
- </div>
- </div>
{measures['coverage'] != null && (
<div className="project-card-measure" data-key="coverage">
<div className="project-card-measure-inner">
diff --git a/server/sonar-web/src/main/js/apps/projects/components/ProjectCardRatingMeasure.tsx b/server/sonar-web/src/main/js/apps/projects/components/ProjectCardRatingMeasure.tsx
new file mode 100644
index 00000000000..6ad1796094b
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projects/components/ProjectCardRatingMeasure.tsx
@@ -0,0 +1,59 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 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 IssueTypeIcon from 'sonar-ui-common/components/icons/IssueTypeIcon';
+import Rating from 'sonar-ui-common/components/ui/Rating';
+import Measure from '../../../components/measure/Measure';
+
+export interface ProjectCardRatingMeasureProps {
+ iconKey?: string;
+ iconLabel: string;
+ measures: T.Dict<string | undefined>;
+ metricKey: string;
+ metricRatingKey: string;
+ metricType: string;
+}
+
+export default function ProjectCardRatingMeasure(props: ProjectCardRatingMeasureProps) {
+ const { iconKey, iconLabel, measures, metricKey, metricRatingKey, metricType } = props;
+
+ return (
+ <div className="project-card-measure" data-key={metricRatingKey}>
+ <div className="project-card-measure-inner">
+ <div className="project-card-measure-number">
+ <Measure
+ className="spacer-right"
+ metricKey={metricKey}
+ metricType={metricType}
+ value={measures[metricKey]}
+ />
+
+ <Rating value={measures[metricRatingKey]} />
+ </div>
+
+ <div className="project-card-measure-label-with-icon">
+ <IssueTypeIcon className="little-spacer-right text-bottom" query={iconKey || metricKey} />
+
+ {iconLabel}
+ </div>
+ </div>
+ </div>
+ );
+}
diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCardRatingMeasure-test.tsx b/server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCardRatingMeasure-test.tsx
new file mode 100644
index 00000000000..58f4dd65714
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCardRatingMeasure-test.tsx
@@ -0,0 +1,47 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 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 { shallow } from 'enzyme';
+import * as React from 'react';
+import ProjectCardRatingMeasure, {
+ ProjectCardRatingMeasureProps
+} from '../ProjectCardRatingMeasure';
+
+it('renders', () => {
+ expect(shallowRender()).toMatchSnapshot();
+ expect(shallowRender({ iconKey: 'overriddenIconKey' })).toMatchSnapshot('iconKey');
+});
+
+function shallowRender(overrides: Partial<ProjectCardRatingMeasureProps> = {}) {
+ const measures = {
+ security_rating: '1',
+ vulnerabilities: '0',
+ dummyThatShouldBeIgnored: 'yes'
+ };
+ return shallow(
+ <ProjectCardRatingMeasure
+ iconLabel="label"
+ measures={measures}
+ metricKey="vulnerabilities"
+ metricRatingKey="security_rating"
+ metricType="SHORT_INT"
+ {...overrides}
+ />
+ );
+}
diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardLeakMeasures-test.tsx.snap b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardLeakMeasures-test.tsx.snap
index 4b2ff55782e..cf796735609 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardLeakMeasures-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardLeakMeasures-test.tsx.snap
@@ -4,123 +4,87 @@ exports[`should render correctly with all data 1`] = `
<div
className="project-card-leak-measures"
>
- <div
- className="project-card-measure"
- data-key="new_reliability_rating"
- >
- <div
- className="project-card-measure-inner"
- >
- <div
- className="project-card-measure-number"
- >
- <Measure
- className="spacer-right"
- metricKey="new_bugs"
- metricType="SHORT_INT"
- value="8"
- />
- <Rating
- value="1.0"
- />
- </div>
- <div
- className="project-card-measure-label-with-icon"
- >
- <BugIcon
- className="little-spacer-right text-bottom"
- />
- metric.bugs.name
- </div>
- </div>
- </div>
- <div
- className="project-card-measure"
- data-key="new_security_rating"
- >
- <div
- className="project-card-measure-inner"
- >
- <div
- className="project-card-measure-number"
- >
- <Measure
- className="spacer-right"
- metricKey="new_vulnerabilities"
- metricType="SHORT_INT"
- value="2"
- />
- <Rating
- value="2.0"
- />
- </div>
- <div
- className="project-card-measure-label-with-icon"
- >
- <VulnerabilityIcon
- className="little-spacer-right text-bottom"
- />
- metric.vulnerabilities.name
- </div>
- </div>
- </div>
- <div
- className="project-card-measure"
- data-key="new_security_review_rating"
- >
- <div
- className="project-card-measure-inner"
- >
- <div
- className="project-card-measure-number"
- >
- <Measure
- className="spacer-right"
- metricKey="new_security_hotspots_reviewed"
- metricType="PERCENT"
- />
- <Rating />
- </div>
- <div
- className="project-card-measure-label-with-icon"
- >
- <SecurityHotspotIcon
- className="little-spacer-right text-bottom"
- />
- metric.security_hotspots_reviewed.extra_short_name
- </div>
- </div>
- </div>
- <div
- className="project-card-measure"
- data-key="new_maintainability_rating"
- >
- <div
- className="project-card-measure-inner"
- >
- <div
- className="project-card-measure-number"
- >
- <Measure
- className="spacer-right"
- metricKey="new_code_smells"
- metricType="SHORT_INT"
- value="0"
- />
- <Rating
- value="1.0"
- />
- </div>
- <div
- className="project-card-measure-label-with-icon"
- >
- <CodeSmellIcon
- className="little-spacer-right text-bottom"
- />
- metric.code_smells.name
- </div>
- </div>
- </div>
+ <ProjectCardRatingMeasure
+ iconLabel="metric.bugs.name"
+ measures={
+ Object {
+ "alert_status": "ERROR",
+ "new_bugs": "8",
+ "new_code_smells": "0",
+ "new_coverage": "26.55",
+ "new_duplicated_lines_density": "0.55",
+ "new_lines": "87",
+ "new_maintainability_rating": "1.0",
+ "new_reliability_rating": "1.0",
+ "new_security_rating": "2.0",
+ "new_vulnerabilities": "2",
+ }
+ }
+ metricKey="new_bugs"
+ metricRatingKey="new_reliability_rating"
+ metricType="SHORT_INT"
+ />
+ <ProjectCardRatingMeasure
+ iconLabel="metric.vulnerabilities.name"
+ measures={
+ Object {
+ "alert_status": "ERROR",
+ "new_bugs": "8",
+ "new_code_smells": "0",
+ "new_coverage": "26.55",
+ "new_duplicated_lines_density": "0.55",
+ "new_lines": "87",
+ "new_maintainability_rating": "1.0",
+ "new_reliability_rating": "1.0",
+ "new_security_rating": "2.0",
+ "new_vulnerabilities": "2",
+ }
+ }
+ metricKey="new_vulnerabilities"
+ metricRatingKey="new_security_rating"
+ metricType="SHORT_INT"
+ />
+ <ProjectCardRatingMeasure
+ iconKey="security_hotspots"
+ iconLabel="projects.security_hotspots_reviewed"
+ measures={
+ Object {
+ "alert_status": "ERROR",
+ "new_bugs": "8",
+ "new_code_smells": "0",
+ "new_coverage": "26.55",
+ "new_duplicated_lines_density": "0.55",
+ "new_lines": "87",
+ "new_maintainability_rating": "1.0",
+ "new_reliability_rating": "1.0",
+ "new_security_rating": "2.0",
+ "new_vulnerabilities": "2",
+ }
+ }
+ metricKey="new_security_hotspots_reviewed"
+ metricRatingKey="new_security_review_rating"
+ metricType="PERCENT"
+ />
+ <ProjectCardRatingMeasure
+ iconLabel="metric.code_smells.name"
+ measures={
+ Object {
+ "alert_status": "ERROR",
+ "new_bugs": "8",
+ "new_code_smells": "0",
+ "new_coverage": "26.55",
+ "new_duplicated_lines_density": "0.55",
+ "new_lines": "87",
+ "new_maintainability_rating": "1.0",
+ "new_reliability_rating": "1.0",
+ "new_security_rating": "2.0",
+ "new_vulnerabilities": "2",
+ }
+ }
+ metricKey="new_code_smells"
+ metricRatingKey="new_maintainability_rating"
+ metricType="SHORT_INT"
+ />
<div
className="project-card-measure"
data-key="new_coverage"
@@ -197,123 +161,75 @@ exports[`should render no data style new coverage, new duplications and new line
<div
className="project-card-leak-measures"
>
- <div
- className="project-card-measure"
- data-key="new_reliability_rating"
- >
- <div
- className="project-card-measure-inner"
- >
- <div
- className="project-card-measure-number"
- >
- <Measure
- className="spacer-right"
- metricKey="new_bugs"
- metricType="SHORT_INT"
- value="8"
- />
- <Rating
- value="1.0"
- />
- </div>
- <div
- className="project-card-measure-label-with-icon"
- >
- <BugIcon
- className="little-spacer-right text-bottom"
- />
- metric.bugs.name
- </div>
- </div>
- </div>
- <div
- className="project-card-measure"
- data-key="new_security_rating"
- >
- <div
- className="project-card-measure-inner"
- >
- <div
- className="project-card-measure-number"
- >
- <Measure
- className="spacer-right"
- metricKey="new_vulnerabilities"
- metricType="SHORT_INT"
- value="2"
- />
- <Rating
- value="2.0"
- />
- </div>
- <div
- className="project-card-measure-label-with-icon"
- >
- <VulnerabilityIcon
- className="little-spacer-right text-bottom"
- />
- metric.vulnerabilities.name
- </div>
- </div>
- </div>
- <div
- className="project-card-measure"
- data-key="new_security_review_rating"
- >
- <div
- className="project-card-measure-inner"
- >
- <div
- className="project-card-measure-number"
- >
- <Measure
- className="spacer-right"
- metricKey="new_security_hotspots_reviewed"
- metricType="PERCENT"
- />
- <Rating />
- </div>
- <div
- className="project-card-measure-label-with-icon"
- >
- <SecurityHotspotIcon
- className="little-spacer-right text-bottom"
- />
- metric.security_hotspots_reviewed.extra_short_name
- </div>
- </div>
- </div>
- <div
- className="project-card-measure"
- data-key="new_maintainability_rating"
- >
- <div
- className="project-card-measure-inner"
- >
- <div
- className="project-card-measure-number"
- >
- <Measure
- className="spacer-right"
- metricKey="new_code_smells"
- metricType="SHORT_INT"
- value="0"
- />
- <Rating
- value="1.0"
- />
- </div>
- <div
- className="project-card-measure-label-with-icon"
- >
- <CodeSmellIcon
- className="little-spacer-right text-bottom"
- />
- metric.code_smells.name
- </div>
- </div>
- </div>
+ <ProjectCardRatingMeasure
+ iconLabel="metric.bugs.name"
+ measures={
+ Object {
+ "alert_status": "ERROR",
+ "new_bugs": "8",
+ "new_code_smells": "0",
+ "new_maintainability_rating": "1.0",
+ "new_reliability_rating": "1.0",
+ "new_security_rating": "2.0",
+ "new_vulnerabilities": "2",
+ }
+ }
+ metricKey="new_bugs"
+ metricRatingKey="new_reliability_rating"
+ metricType="SHORT_INT"
+ />
+ <ProjectCardRatingMeasure
+ iconLabel="metric.vulnerabilities.name"
+ measures={
+ Object {
+ "alert_status": "ERROR",
+ "new_bugs": "8",
+ "new_code_smells": "0",
+ "new_maintainability_rating": "1.0",
+ "new_reliability_rating": "1.0",
+ "new_security_rating": "2.0",
+ "new_vulnerabilities": "2",
+ }
+ }
+ metricKey="new_vulnerabilities"
+ metricRatingKey="new_security_rating"
+ metricType="SHORT_INT"
+ />
+ <ProjectCardRatingMeasure
+ iconKey="security_hotspots"
+ iconLabel="projects.security_hotspots_reviewed"
+ measures={
+ Object {
+ "alert_status": "ERROR",
+ "new_bugs": "8",
+ "new_code_smells": "0",
+ "new_maintainability_rating": "1.0",
+ "new_reliability_rating": "1.0",
+ "new_security_rating": "2.0",
+ "new_vulnerabilities": "2",
+ }
+ }
+ metricKey="new_security_hotspots_reviewed"
+ metricRatingKey="new_security_review_rating"
+ metricType="PERCENT"
+ />
+ <ProjectCardRatingMeasure
+ iconLabel="metric.code_smells.name"
+ measures={
+ Object {
+ "alert_status": "ERROR",
+ "new_bugs": "8",
+ "new_code_smells": "0",
+ "new_maintainability_rating": "1.0",
+ "new_reliability_rating": "1.0",
+ "new_security_rating": "2.0",
+ "new_vulnerabilities": "2",
+ }
+ }
+ metricKey="new_code_smells"
+ metricRatingKey="new_maintainability_rating"
+ metricType="SHORT_INT"
+ />
<div
className="project-card-measure"
data-key="new_duplicated_lines_density"
diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardOverallMeasures-test.tsx.snap b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardOverallMeasures-test.tsx.snap
index c8d30038bea..23088af1a46 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardOverallMeasures-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardOverallMeasures-test.tsx.snap
@@ -4,123 +4,87 @@ exports[`should render correctly with all data 1`] = `
<div
className="project-card-measures"
>
- <div
- className="project-card-measure"
- data-key="reliability_rating"
- >
- <div
- className="project-card-measure-inner"
- >
- <div
- className="project-card-measure-number"
- >
- <Measure
- className="spacer-right"
- metricKey="bugs"
- metricType="SHORT_INT"
- value="17"
- />
- <Rating
- value="1.0"
- />
- </div>
- <div
- className="project-card-measure-label-with-icon"
- >
- <BugIcon
- className="little-spacer-right text-bottom"
- />
- metric.bugs.name
- </div>
- </div>
- </div>
- <div
- className="project-card-measure"
- data-key="security_rating"
- >
- <div
- className="project-card-measure-inner"
- >
- <div
- className="project-card-measure-number"
- >
- <Measure
- className="spacer-right"
- metricKey="vulnerabilities"
- metricType="SHORT_INT"
- value="0"
- />
- <Rating
- value="1.0"
- />
- </div>
- <div
- className="project-card-measure-label-with-icon"
- >
- <VulnerabilityIcon
- className="little-spacer-right text-bottom"
- />
- metric.vulnerabilities.name
- </div>
- </div>
- </div>
- <div
- className="project-card-measure"
- data-key="security_review_rating"
- >
- <div
- className="project-card-measure-inner"
- >
- <div
- className="project-card-measure-number"
- >
- <Measure
- className="spacer-right"
- metricKey="security_hotspots_reviewed"
- metricType="PERCENT"
- />
- <Rating />
- </div>
- <div
- className="project-card-measure-label-with-icon"
- >
- <SecurityHotspotIcon
- className="little-spacer-right text-bottom"
- />
- metric.security_hotspots_reviewed.extra_short_name
- </div>
- </div>
- </div>
- <div
- className="project-card-measure"
- data-key="sqale_rating"
- >
- <div
- className="project-card-measure-inner"
- >
- <div
- className="project-card-measure-number"
- >
- <Measure
- className="spacer-right"
- metricKey="code_smells"
- metricType="SHORT_INT"
- value="132"
- />
- <Rating
- value="1.0"
- />
- </div>
- <div
- className="project-card-measure-label-with-icon"
- >
- <CodeSmellIcon
- className="little-spacer-right text-bottom"
- />
- metric.code_smells.name
- </div>
- </div>
- </div>
+ <ProjectCardRatingMeasure
+ iconLabel="metric.bugs.name"
+ measures={
+ Object {
+ "alert_status": "ERROR",
+ "bugs": "17",
+ "code_smells": "132",
+ "coverage": "88.3",
+ "duplicated_lines_density": "9.8",
+ "ncloc": "2053",
+ "reliability_rating": "1.0",
+ "security_rating": "1.0",
+ "sqale_rating": "1.0",
+ "vulnerabilities": "0",
+ }
+ }
+ metricKey="bugs"
+ metricRatingKey="reliability_rating"
+ metricType="SHORT_INT"
+ />
+ <ProjectCardRatingMeasure
+ iconLabel="metric.vulnerabilities.name"
+ measures={
+ Object {
+ "alert_status": "ERROR",
+ "bugs": "17",
+ "code_smells": "132",
+ "coverage": "88.3",
+ "duplicated_lines_density": "9.8",
+ "ncloc": "2053",
+ "reliability_rating": "1.0",
+ "security_rating": "1.0",
+ "sqale_rating": "1.0",
+ "vulnerabilities": "0",
+ }
+ }
+ metricKey="vulnerabilities"
+ metricRatingKey="security_rating"
+ metricType="SHORT_INT"
+ />
+ <ProjectCardRatingMeasure
+ iconKey="security_hotspots"
+ iconLabel="projects.security_hotspots_reviewed"
+ measures={
+ Object {
+ "alert_status": "ERROR",
+ "bugs": "17",
+ "code_smells": "132",
+ "coverage": "88.3",
+ "duplicated_lines_density": "9.8",
+ "ncloc": "2053",
+ "reliability_rating": "1.0",
+ "security_rating": "1.0",
+ "sqale_rating": "1.0",
+ "vulnerabilities": "0",
+ }
+ }
+ metricKey="security_hotspots_reviewed"
+ metricRatingKey="security_review_rating"
+ metricType="PERCENT"
+ />
+ <ProjectCardRatingMeasure
+ iconLabel="metric.code_smells.name"
+ measures={
+ Object {
+ "alert_status": "ERROR",
+ "bugs": "17",
+ "code_smells": "132",
+ "coverage": "88.3",
+ "duplicated_lines_density": "9.8",
+ "ncloc": "2053",
+ "reliability_rating": "1.0",
+ "security_rating": "1.0",
+ "sqale_rating": "1.0",
+ "vulnerabilities": "0",
+ }
+ }
+ metricKey="code_smells"
+ metricRatingKey="sqale_rating"
+ metricType="SHORT_INT"
+ />
<div
className="project-card-measure"
data-key="coverage"
diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardRatingMeasure-test.tsx.snap b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardRatingMeasure-test.tsx.snap
new file mode 100644
index 00000000000..730efde8faa
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardRatingMeasure-test.tsx.snap
@@ -0,0 +1,69 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`renders 1`] = `
+<div
+ className="project-card-measure"
+ data-key="security_rating"
+>
+ <div
+ className="project-card-measure-inner"
+ >
+ <div
+ className="project-card-measure-number"
+ >
+ <Measure
+ className="spacer-right"
+ metricKey="vulnerabilities"
+ metricType="SHORT_INT"
+ value="0"
+ />
+ <Rating
+ value="1"
+ />
+ </div>
+ <div
+ className="project-card-measure-label-with-icon"
+ >
+ <IssueTypeIcon
+ className="little-spacer-right text-bottom"
+ query="vulnerabilities"
+ />
+ label
+ </div>
+ </div>
+</div>
+`;
+
+exports[`renders: iconKey 1`] = `
+<div
+ className="project-card-measure"
+ data-key="security_rating"
+>
+ <div
+ className="project-card-measure-inner"
+ >
+ <div
+ className="project-card-measure-number"
+ >
+ <Measure
+ className="spacer-right"
+ metricKey="vulnerabilities"
+ metricType="SHORT_INT"
+ value="0"
+ />
+ <Rating
+ value="1"
+ />
+ </div>
+ <div
+ className="project-card-measure-label-with-icon"
+ >
+ <IssueTypeIcon
+ className="little-spacer-right text-bottom"
+ query="overriddenIconKey"
+ />
+ label
+ </div>
+ </div>
+</div>
+`;
diff --git a/server/sonar-web/src/main/js/helpers/urls.ts b/server/sonar-web/src/main/js/helpers/urls.ts
index 21cac8461c2..ad1e3e5c04e 100644
--- a/server/sonar-web/src/main/js/helpers/urls.ts
+++ b/server/sonar-web/src/main/js/helpers/urls.ts
@@ -77,8 +77,8 @@ export function getComponentIssuesUrl(componentKey: string, query?: Query): Loca
/**
* Generate URL for a component's security hotspot page
*/
-export function getComponentSecurityHotspotsUrl(componentKey: string): Location {
- return { pathname: '/security_hotspots', query: { id: componentKey } };
+export function getComponentSecurityHotspotsUrl(componentKey: string, query: Query = {}): Location {
+ return { pathname: '/security_hotspots', query: { ...query, id: componentKey } };
}
/**
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 f49347aa30c..45e6844cc53 100644
--- a/sonar-core/src/main/resources/org/sonar/l10n/core.properties
+++ b/sonar-core/src/main/resources/org/sonar/l10n/core.properties
@@ -968,6 +968,7 @@ projects.sort.-duplications=by duplications (worst first)
projects.sort.size=by size (smallest first)
projects.sort.-size=by size (biggest first)
+projects.security_hotspots_reviewed=Hotspots Reviewed
#------------------------------------------------------------------------------
#
@@ -2223,7 +2224,10 @@ metric.security_hotspots.description=Security Hotspots
metric.security_hotspots.name=Security Hotspots
metric.security_hotspots_reviewed.description=Percentage of Security Hotspots Reviewed
metric.security_hotspots_reviewed.name=Security Hotspots Reviewed
-metric.security_hotspots_reviewed.extra_short_name=Hotspots Reviewed
+metric.security_hotspots_reviewed_status.description=Security Review Reviewed Status
+metric.security_hotspots_reviewed_status.name=Security Review Reviewed Status
+metric.security_hotspots_to_review_status.description=Security Review To Review Status
+metric.security_hotspots_to_review_status.name=Security Review To Review Status
metric.security_rating.description=Security rating
metric.security_rating.name=Security Rating
metric.security_rating.extra_short_name=Rating