From 0a1d327928f35e177f3190d810105eb7e661ebc7 Mon Sep 17 00:00:00 2001 From: Mathieu Suen Date: Fri, 31 Jan 2020 15:15:36 +0100 Subject: SONAR-12981 Add security review rating and hotspots in PR overview. --- .../js/apps/overview/components/IssueLabel.tsx | 8 +- .../js/apps/overview/components/IssueRating.tsx | 42 ++- .../components/__tests__/IssueRating-test.tsx | 4 +- .../__snapshots__/IssueLabel-test.tsx.snap | 10 +- .../__snapshots__/IssueRating-test.tsx.snap | 15 + .../overview/pullRequests/PullRequestOverview.tsx | 63 ++-- .../PullRequestOverview-test.tsx.snap | 246 ++++++++++++- .../sonar-web/src/main/js/apps/overview/utils.ts | 8 +- .../components/ProjectCardLeakMeasures.tsx | 103 ++---- .../components/ProjectCardOverallMeasures.tsx | 104 ++---- .../components/ProjectCardRatingMeasure.tsx | 59 ++++ .../__tests__/ProjectCardRatingMeasure-test.tsx | 47 +++ .../ProjectCardLeakMeasures-test.tsx.snap | 384 ++++++++------------- .../ProjectCardOverallMeasures-test.tsx.snap | 198 +++++------ .../ProjectCardRatingMeasure-test.tsx.snap | 69 ++++ server/sonar-web/src/main/js/helpers/urls.ts | 4 +- .../main/resources/org/sonar/l10n/core.properties | 6 +- 17 files changed, 797 insertions(+), 573 deletions(-) create mode 100644 server/sonar-web/src/main/js/apps/projects/components/ProjectCardRatingMeasure.tsx create mode 100644 server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCardRatingMeasure-test.tsx create mode 100644 server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardRatingMeasure-test.tsx.snap 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) { ) : ( + to={ + type === IssueType.SecurityHotspot + ? getComponentSecurityHotspotsUrl(component.key, getBranchLikeQuery(branchLike)) + : getComponentIssuesUrl(component.key, params) + }> {formatMeasure(value, 'SHORT_INT')} )} 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 ( +
+ +
+ ); } - const value = useDiffMetric ? getLeakValue(measure) : measure.value; + const value = measure && (useDiffMetric ? getLeakValue(measure) : measure.value); const tooltip = value && getRatingTooltip(rating, Number(value)); + return ( + + + + + + + + ); +} + +export function IssueRating(props: IssueRatingProps) { + const { type } = props; + return ( <> {getIssueRatingName(type)} - - - - - - - + {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 = {}) { 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`] = ` `; + +exports[`should render correctly if no values are present 1`] = ` + + + metric_domain.Reliability + +
+ +
+
+`; 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 {
- {[IssueType.Bug, IssueType.Vulnerability, IssueType.CodeSmell].map( - (type: IssueType) => ( -
-
- -
- {type === 'VULNERABILITY' && ( -
- -
- )} -
- -
+ {[ + IssueType.Bug, + IssueType.Vulnerability, + IssueType.SecurityHotspot, + IssueType.CodeSmell + ].map((type: IssueType) => ( +
+
+
- ) - )} +
+ +
+
+ ))} {[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`] = ` />
+ +
+
+
+
@@ -1745,7 +1862,125 @@ exports[`should render correctly for a passed QG 1`] = ` />
+ +
+ +
+
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; @@ -33,77 +29,38 @@ interface Props { export default function ProjectCardLeakMeasures({ measures }: Props) { return (
-
-
-
- - -
-
- - {translate('metric.bugs.name')} -
-
-
+ -
-
-
- - -
-
- - {translate('metric.vulnerabilities.name')} -
-
-
+ -
-
-
- - -
-
- - {translate('metric.security_hotspots_reviewed.extra_short_name')} -
-
-
+ -
-
-
- - -
-
- - {translate('metric.code_smells.name')} -
-
-
+ {measures['new_coverage'] != null && (
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; @@ -46,77 +42,39 @@ export default function ProjectCardOverallMeasures({ measures }: Props) { return (
-
-
-
- - -
-
- - {translate('metric.bugs.name')} -
-
-
+ -
-
-
- - -
-
- - {translate('metric.vulnerabilities.name')} -
-
-
+ -
-
-
- - -
-
- - {translate('metric.security_hotspots_reviewed.extra_short_name')} -
-
-
+ + + -
-
-
- - -
-
- - {translate('metric.code_smells.name')} -
-
-
{measures['coverage'] != null && (
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; + metricKey: string; + metricRatingKey: string; + metricType: string; +} + +export default function ProjectCardRatingMeasure(props: ProjectCardRatingMeasureProps) { + const { iconKey, iconLabel, measures, metricKey, metricRatingKey, metricType } = props; + + return ( +
+
+
+ + + +
+ +
+ + + {iconLabel} +
+
+
+ ); +} 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 = {}) { + const measures = { + security_rating: '1', + vulnerabilities: '0', + dummyThatShouldBeIgnored: 'yes' + }; + return shallow( + + ); +} 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`] = `
-
-
-
- - -
-
- - metric.bugs.name -
-
-
-
-
-
- - -
-
- - metric.vulnerabilities.name -
-
-
-
-
-
- - -
-
- - metric.security_hotspots_reviewed.extra_short_name -
-
-
-
-
-
- - -
-
- - metric.code_smells.name -
-
-
+ + + +
-
-
-
- - -
-
- - metric.bugs.name -
-
-
-
-
-
- - -
-
- - metric.vulnerabilities.name -
-
-
-
-
-
- - -
-
- - metric.security_hotspots_reviewed.extra_short_name -
-
-
-
-
-
- - -
-
- - metric.code_smells.name -
-
-
+ + + +
-
-
-
- - -
-
- - metric.bugs.name -
-
-
-
-
-
- - -
-
- - metric.vulnerabilities.name -
-
-
-
-
-
- - -
-
- - metric.security_hotspots_reviewed.extra_short_name -
-
-
-
-
-
- - -
-
- - metric.code_smells.name -
-
-
+ + + +
+
+
+ + +
+
+ + label +
+
+
+`; + +exports[`renders: iconKey 1`] = ` +
+
+
+ + +
+
+ + label +
+
+
+`; 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 -- cgit v1.2.3