aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGrégoire Aubert <gregoire.aubert@sonarsource.com>2019-04-09 13:55:26 +0200
committerSonarTech <sonartech@sonarsource.com>2019-04-23 20:21:09 +0200
commitb92d5bf5496c2ea1705cc07de0228e2a868f926e (patch)
treef1d9dd3814bece332a3bc912fcd39b7d5350bd88
parent4f885e88db04134cea9d500e89f28e6a09b65db2 (diff)
downloadsonarqube-b92d5bf5496c2ea1705cc07de0228e2a868f926e.tar.gz
sonarqube-b92d5bf5496c2ea1705cc07de0228e2a868f926e.zip
SONAR-11885 Add Hotspots to Overview page
* Update Measures links on Overview page * Add tooltips to the issues and metrics on the Overview page * Move activity page links on the Overview page * Add tests for untested components * Add bugs and vulnerabilities graphs on overview page
-rw-r--r--server/sonar-docs/src/tooltips/metrics/bugs.md1
-rw-r--r--server/sonar-docs/src/tooltips/metrics/code-smells.md1
-rw-r--r--server/sonar-docs/src/tooltips/metrics/coverage.md1
-rw-r--r--server/sonar-docs/src/tooltips/metrics/debt.md1
-rw-r--r--server/sonar-docs/src/tooltips/metrics/duplicated-blocks.md1
-rw-r--r--server/sonar-docs/src/tooltips/metrics/duplications.md1
-rw-r--r--server/sonar-docs/src/tooltips/metrics/security-hotspots.md1
-rw-r--r--server/sonar-docs/src/tooltips/metrics/unit-tests.md1
-rw-r--r--server/sonar-docs/src/tooltips/metrics/vulnerabilities.md1
-rw-r--r--server/sonar-web/src/main/js/app/styles/init/misc.css5
-rw-r--r--server/sonar-web/src/main/js/apps/overview/components/OverviewApp.tsx6
-rw-r--r--server/sonar-web/src/main/js/apps/overview/components/__tests__/OverviewApp-test.tsx22
-rw-r--r--server/sonar-web/src/main/js/apps/overview/components/__tests__/__snapshots__/OverviewApp-test.tsx.snap199
-rw-r--r--server/sonar-web/src/main/js/apps/overview/main/Bugs.tsx (renamed from server/sonar-web/src/main/js/apps/overview/main/BugsAndVulnerabilities.tsx)94
-rw-r--r--server/sonar-web/src/main/js/apps/overview/main/CodeSmells.tsx52
-rw-r--r--server/sonar-web/src/main/js/apps/overview/main/Coverage.tsx26
-rw-r--r--server/sonar-web/src/main/js/apps/overview/main/Duplications.tsx21
-rw-r--r--server/sonar-web/src/main/js/apps/overview/main/VulnerabilitiesAndHotspots.tsx127
-rw-r--r--server/sonar-web/src/main/js/apps/overview/main/__tests__/Bugs-test.tsx87
-rw-r--r--server/sonar-web/src/main/js/apps/overview/main/__tests__/CodeSmells-test.tsx113
-rw-r--r--server/sonar-web/src/main/js/apps/overview/main/__tests__/Coverage-test.tsx73
-rw-r--r--server/sonar-web/src/main/js/apps/overview/main/__tests__/Duplications-test.tsx92
-rw-r--r--server/sonar-web/src/main/js/apps/overview/main/__tests__/VulnerabilitiesAndHotspots-test.tsx119
-rw-r--r--server/sonar-web/src/main/js/apps/overview/main/__tests__/__snapshots__/Bugs-test.tsx.snap180
-rw-r--r--server/sonar-web/src/main/js/apps/overview/main/__tests__/__snapshots__/CodeSmells-test.tsx.snap223
-rw-r--r--server/sonar-web/src/main/js/apps/overview/main/__tests__/__snapshots__/Coverage-test.tsx.snap187
-rw-r--r--server/sonar-web/src/main/js/apps/overview/main/__tests__/__snapshots__/Duplications-test.tsx.snap166
-rw-r--r--server/sonar-web/src/main/js/apps/overview/main/__tests__/__snapshots__/VulnerabilitiesAndHotspots-test.tsx.snap288
-rw-r--r--server/sonar-web/src/main/js/apps/overview/main/enhance.tsx27
-rw-r--r--server/sonar-web/src/main/js/apps/overview/styles.css60
-rw-r--r--server/sonar-web/src/main/js/apps/overview/utils.ts4
-rwxr-xr-xserver/sonar-web/src/main/js/apps/securityReports/components/VulnerabilityList.tsx2
-rw-r--r--server/sonar-web/src/main/js/apps/securityReports/components/__tests__/__snapshots__/VulnerabilityList-test.tsx.snap4
-rw-r--r--server/sonar-web/src/main/js/helpers/testMocks.ts17
-rw-r--r--sonar-core/src/main/resources/org/sonar/l10n/core.properties4
35 files changed, 2029 insertions, 178 deletions
diff --git a/server/sonar-docs/src/tooltips/metrics/bugs.md b/server/sonar-docs/src/tooltips/metrics/bugs.md
new file mode 100644
index 00000000000..ff8e346d96b
--- /dev/null
+++ b/server/sonar-docs/src/tooltips/metrics/bugs.md
@@ -0,0 +1 @@
+A coding error that will break your code and needs to be fixed immediately. \ No newline at end of file
diff --git a/server/sonar-docs/src/tooltips/metrics/code-smells.md b/server/sonar-docs/src/tooltips/metrics/code-smells.md
new file mode 100644
index 00000000000..95a031e8373
--- /dev/null
+++ b/server/sonar-docs/src/tooltips/metrics/code-smells.md
@@ -0,0 +1 @@
+Code that is confusing and difficult to maintain. \ No newline at end of file
diff --git a/server/sonar-docs/src/tooltips/metrics/coverage.md b/server/sonar-docs/src/tooltips/metrics/coverage.md
new file mode 100644
index 00000000000..28784cdc084
--- /dev/null
+++ b/server/sonar-docs/src/tooltips/metrics/coverage.md
@@ -0,0 +1 @@
+The percentage of lines of code covered by tests. \ No newline at end of file
diff --git a/server/sonar-docs/src/tooltips/metrics/debt.md b/server/sonar-docs/src/tooltips/metrics/debt.md
new file mode 100644
index 00000000000..8b6141bf74b
--- /dev/null
+++ b/server/sonar-docs/src/tooltips/metrics/debt.md
@@ -0,0 +1 @@
+The estimated time it will take to fix all Code Smells. \ No newline at end of file
diff --git a/server/sonar-docs/src/tooltips/metrics/duplicated-blocks.md b/server/sonar-docs/src/tooltips/metrics/duplicated-blocks.md
new file mode 100644
index 00000000000..0458c5f7df1
--- /dev/null
+++ b/server/sonar-docs/src/tooltips/metrics/duplicated-blocks.md
@@ -0,0 +1 @@
+The number of duplicated blocks of lines of code. \ No newline at end of file
diff --git a/server/sonar-docs/src/tooltips/metrics/duplications.md b/server/sonar-docs/src/tooltips/metrics/duplications.md
new file mode 100644
index 00000000000..31ae4afde25
--- /dev/null
+++ b/server/sonar-docs/src/tooltips/metrics/duplications.md
@@ -0,0 +1 @@
+Identical lines of code. \ No newline at end of file
diff --git a/server/sonar-docs/src/tooltips/metrics/security-hotspots.md b/server/sonar-docs/src/tooltips/metrics/security-hotspots.md
new file mode 100644
index 00000000000..a9460e893e0
--- /dev/null
+++ b/server/sonar-docs/src/tooltips/metrics/security-hotspots.md
@@ -0,0 +1 @@
+Security-sensitive code that requires manual review to assess whether or not a vulnerability exists.
diff --git a/server/sonar-docs/src/tooltips/metrics/unit-tests.md b/server/sonar-docs/src/tooltips/metrics/unit-tests.md
new file mode 100644
index 00000000000..96fb4ef3663
--- /dev/null
+++ b/server/sonar-docs/src/tooltips/metrics/unit-tests.md
@@ -0,0 +1 @@
+Tests that ensure your code is working properly. \ No newline at end of file
diff --git a/server/sonar-docs/src/tooltips/metrics/vulnerabilities.md b/server/sonar-docs/src/tooltips/metrics/vulnerabilities.md
new file mode 100644
index 00000000000..f7845ac883e
--- /dev/null
+++ b/server/sonar-docs/src/tooltips/metrics/vulnerabilities.md
@@ -0,0 +1 @@
+Code that can be exploited by hackers. \ No newline at end of file
diff --git a/server/sonar-web/src/main/js/app/styles/init/misc.css b/server/sonar-web/src/main/js/app/styles/init/misc.css
index 06637e62f6d..bbe084f4229 100644
--- a/server/sonar-web/src/main/js/app/styles/init/misc.css
+++ b/server/sonar-web/src/main/js/app/styles/init/misc.css
@@ -307,6 +307,11 @@ td.big-spacer-top {
align-items: center;
}
+.display-flex-justify-center {
+ display: flex !important;
+ justify-content: center;
+}
+
.display-flex-space-around {
display: flex !important;
justify-content: space-around;
diff --git a/server/sonar-web/src/main/js/apps/overview/components/OverviewApp.tsx b/server/sonar-web/src/main/js/apps/overview/components/OverviewApp.tsx
index 59c6ff4fefd..be4107412cc 100644
--- a/server/sonar-web/src/main/js/apps/overview/components/OverviewApp.tsx
+++ b/server/sonar-web/src/main/js/apps/overview/components/OverviewApp.tsx
@@ -21,10 +21,11 @@ import * as React from 'react';
import { uniq } from 'lodash';
import { connect } from 'react-redux';
import ApplicationQualityGate from '../qualityGate/ApplicationQualityGate';
-import BugsAndVulnerabilities from '../main/BugsAndVulnerabilities';
+import Bugs from '../main/Bugs';
import CodeSmells from '../main/CodeSmells';
import Coverage from '../main/Coverage';
import Duplications from '../main/Duplications';
+import VulnerabilitiesAndHotspots from '../main/VulnerabilitiesAndHotspots';
import MetaContainer from '../meta/MetaContainer';
import QualityGate from '../qualityGate/QualityGate';
import A11ySkipTarget from '../../../app/components/a11y/A11ySkipTarget';
@@ -213,7 +214,8 @@ export class OverviewApp extends React.PureComponent<Props, State> {
)}
<div className="overview-domains-list">
- <BugsAndVulnerabilities {...domainProps} />
+ <Bugs {...domainProps} />
+ <VulnerabilitiesAndHotspots {...domainProps} />
<CodeSmells {...domainProps} />
<Coverage {...domainProps} />
<Duplications {...domainProps} />
diff --git a/server/sonar-web/src/main/js/apps/overview/components/__tests__/OverviewApp-test.tsx b/server/sonar-web/src/main/js/apps/overview/components/__tests__/OverviewApp-test.tsx
index 84c7cc24557..c107902beb2 100644
--- a/server/sonar-web/src/main/js/apps/overview/components/__tests__/OverviewApp-test.tsx
+++ b/server/sonar-web/src/main/js/apps/overview/components/__tests__/OverviewApp-test.tsx
@@ -50,22 +50,12 @@ jest.mock('../../../../helpers/dates', () => ({
jest.mock('../../../../api/time-machine', () => ({
getAllTimeMachineData: jest.fn().mockResolvedValue({
measures: [
- {
- metric: 'sqale_index',
- history: [{ date: '2019-01-01', value: '1.0' }]
- },
- {
- metric: 'duplicated_lines_density',
- history: [{ date: '2019-01-02', value: '1.0' }]
- },
- {
- metric: 'ncloc',
- history: [{ date: '2019-01-03', value: '10000' }]
- },
- {
- metric: 'coverage',
- history: [{ date: '2019-01-04', value: '95.5' }]
- }
+ { metric: 'bugs', history: [{ date: '2019-01-05', value: '2.0' }] },
+ { metric: 'vulnerabilities', history: [{ date: '2019-01-05', value: '0' }] },
+ { metric: 'sqale_index', history: [{ date: '2019-01-01', value: '1.0' }] },
+ { metric: 'duplicated_lines_density', history: [{ date: '2019-01-02', value: '1.0' }] },
+ { metric: 'ncloc', history: [{ date: '2019-01-03', value: '10000' }] },
+ { metric: 'coverage', history: [{ date: '2019-01-04', value: '95.5' }] }
]
})
}));
diff --git a/server/sonar-web/src/main/js/apps/overview/components/__tests__/__snapshots__/OverviewApp-test.tsx.snap b/server/sonar-web/src/main/js/apps/overview/components/__tests__/__snapshots__/OverviewApp-test.tsx.snap
index 2bc253d326b..b92c643ffa7 100644
--- a/server/sonar-web/src/main/js/apps/overview/components/__tests__/__snapshots__/OverviewApp-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/overview/components/__tests__/__snapshots__/OverviewApp-test.tsx.snap
@@ -96,7 +96,7 @@ exports[`should render correctly 2`] = `
<div
className="overview-domains-list"
>
- <enhance(undefined)}
+ <enhance(Bugs)
branchLike={
Object {
"analysisDate": "2018-01-01",
@@ -129,6 +129,12 @@ exports[`should render correctly 2`] = `
}
history={
Object {
+ "bugs": Array [
+ Object {
+ "date": "2019-01-05",
+ "value": "2.0",
+ },
+ ],
"coverage": Array [
Object {
"date": "2019-01-04",
@@ -153,9 +159,15 @@ exports[`should render correctly 2`] = `
"value": "1.0",
},
],
+ "vulnerabilities": Array [
+ Object {
+ "date": "2019-01-05",
+ "value": "0",
+ },
+ ],
}
}
- historyStartDate="2019-01-01"
+ historyStartDate="2019-01-05"
measures={
Array [
Object {
@@ -195,7 +207,7 @@ exports[`should render correctly 2`] = `
]
}
/>
- <enhance(undefined)}
+ <enhance(VulnerabiltiesAndHotspots)
branchLike={
Object {
"analysisDate": "2018-01-01",
@@ -228,6 +240,12 @@ exports[`should render correctly 2`] = `
}
history={
Object {
+ "bugs": Array [
+ Object {
+ "date": "2019-01-05",
+ "value": "2.0",
+ },
+ ],
"coverage": Array [
Object {
"date": "2019-01-04",
@@ -252,9 +270,15 @@ exports[`should render correctly 2`] = `
"value": "1.0",
},
],
+ "vulnerabilities": Array [
+ Object {
+ "date": "2019-01-05",
+ "value": "0",
+ },
+ ],
}
}
- historyStartDate="2019-01-01"
+ historyStartDate="2019-01-05"
measures={
Array [
Object {
@@ -294,7 +318,7 @@ exports[`should render correctly 2`] = `
]
}
/>
- <enhance(undefined)}
+ <enhance(CodeSmells)
branchLike={
Object {
"analysisDate": "2018-01-01",
@@ -327,6 +351,12 @@ exports[`should render correctly 2`] = `
}
history={
Object {
+ "bugs": Array [
+ Object {
+ "date": "2019-01-05",
+ "value": "2.0",
+ },
+ ],
"coverage": Array [
Object {
"date": "2019-01-04",
@@ -351,9 +381,15 @@ exports[`should render correctly 2`] = `
"value": "1.0",
},
],
+ "vulnerabilities": Array [
+ Object {
+ "date": "2019-01-05",
+ "value": "0",
+ },
+ ],
}
}
- historyStartDate="2019-01-01"
+ historyStartDate="2019-01-05"
measures={
Array [
Object {
@@ -393,7 +429,7 @@ exports[`should render correctly 2`] = `
]
}
/>
- <enhance(undefined)}
+ <enhance(Coverage)
branchLike={
Object {
"analysisDate": "2018-01-01",
@@ -426,6 +462,12 @@ exports[`should render correctly 2`] = `
}
history={
Object {
+ "bugs": Array [
+ Object {
+ "date": "2019-01-05",
+ "value": "2.0",
+ },
+ ],
"coverage": Array [
Object {
"date": "2019-01-04",
@@ -450,9 +492,126 @@ exports[`should render correctly 2`] = `
"value": "1.0",
},
],
+ "vulnerabilities": Array [
+ Object {
+ "date": "2019-01-05",
+ "value": "0",
+ },
+ ],
}
}
- historyStartDate="2019-01-01"
+ historyStartDate="2019-01-05"
+ measures={
+ Array [
+ Object {
+ "bestValue": true,
+ "metric": Object {
+ "id": "coverage",
+ "key": "ncloc",
+ "name": "Coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "metric": Object {
+ "id": "coverage",
+ "key": "coverage",
+ "name": "Coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ ]
+ }
+ />
+ <enhance(Duplications)
+ branchLike={
+ Object {
+ "analysisDate": "2018-01-01",
+ "isMain": true,
+ "name": "master",
+ }
+ }
+ component={
+ Object {
+ "breadcrumbs": Array [],
+ "key": "my-project",
+ "name": "foo",
+ "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 [],
+ }
+ }
+ history={
+ Object {
+ "bugs": Array [
+ Object {
+ "date": "2019-01-05",
+ "value": "2.0",
+ },
+ ],
+ "coverage": Array [
+ Object {
+ "date": "2019-01-04",
+ "value": "95.5",
+ },
+ ],
+ "duplicated_lines_density": Array [
+ Object {
+ "date": "2019-01-02",
+ "value": "1.0",
+ },
+ ],
+ "ncloc": Array [
+ Object {
+ "date": "2019-01-03",
+ "value": "10000",
+ },
+ ],
+ "sqale_index": Array [
+ Object {
+ "date": "2019-01-01",
+ "value": "1.0",
+ },
+ ],
+ "vulnerabilities": Array [
+ Object {
+ "date": "2019-01-05",
+ "value": "0",
+ },
+ ],
+ }
+ }
+ historyStartDate="2019-01-05"
measures={
Array [
Object {
@@ -530,6 +689,12 @@ exports[`should render correctly 2`] = `
}
history={
Object {
+ "bugs": Array [
+ Object {
+ "date": "2019-01-05",
+ "value": "2.0",
+ },
+ ],
"coverage": Array [
Object {
"date": "2019-01-04",
@@ -554,6 +719,12 @@ exports[`should render correctly 2`] = `
"value": "1.0",
},
],
+ "vulnerabilities": Array [
+ Object {
+ "date": "2019-01-05",
+ "value": "0",
+ },
+ ],
}
}
measures={
@@ -664,6 +835,12 @@ exports[`should render correctly if no measures are found 1`] = `
}
history={
Object {
+ "bugs": Array [
+ Object {
+ "date": "2019-01-05",
+ "value": "2.0",
+ },
+ ],
"coverage": Array [
Object {
"date": "2019-01-04",
@@ -688,6 +865,12 @@ exports[`should render correctly if no measures are found 1`] = `
"value": "1.0",
},
],
+ "vulnerabilities": Array [
+ Object {
+ "date": "2019-01-05",
+ "value": "0",
+ },
+ ],
}
}
measures={
diff --git a/server/sonar-web/src/main/js/apps/overview/main/BugsAndVulnerabilities.tsx b/server/sonar-web/src/main/js/apps/overview/main/Bugs.tsx
index 5ef03220d46..e370f3ae8c1 100644
--- a/server/sonar-web/src/main/js/apps/overview/main/BugsAndVulnerabilities.tsx
+++ b/server/sonar-web/src/main/js/apps/overview/main/Bugs.tsx
@@ -18,49 +18,40 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
-import { Link } from 'react-router';
import enhance, { ComposedProps } from './enhance';
import ApplicationLeakPeriodLegend from '../components/ApplicationLeakPeriodLegend';
-import BubblesIcon from '../../../components/icons-components/BubblesIcon';
import BugIcon from '../../../components/icons-components/BugIcon';
+import DocTooltip from '../../../components/docs/DocTooltip';
+import DateFromNow from '../../../components/intl/DateFromNow';
import LeakPeriodLegend from '../components/LeakPeriodLegend';
-import VulnerabilityIcon from '../../../components/icons-components/VulnerabilityIcon';
import { getMetricName } from '../utils';
-import { getComponentDrilldownUrl } from '../../../helpers/urls';
-import { translate } from '../../../helpers/l10n';
import { isLongLivingBranch } from '../../../helpers/branches';
+import { translateWithParameters } from '../../../helpers/l10n';
-export class BugsAndVulnerabilities extends React.PureComponent<ComposedProps> {
+export class Bugs extends React.PureComponent<ComposedProps> {
renderHeader() {
- const { branchLike, component } = this.props;
+ return this.props.renderHeader('Reliability');
+ }
+
+ renderTimelineStartDate(historyStartDate?: Date) {
+ if (!historyStartDate) {
+ return undefined;
+ }
return (
- <div className="overview-card-header">
- <div className="overview-title">
- <span>{translate('metric.bugs.name')}</span>
- <Link
- className="button button-small spacer-left text-text-bottom"
- to={getComponentDrilldownUrl({
- componentKey: component.key,
- metric: 'Reliability',
- branchLike
- })}>
- <BubblesIcon size={14} />
- </Link>
- <span className="big-spacer-left">{translate('metric.vulnerabilities.name')}</span>
- <Link
- className="button button-small spacer-left text-text-bottom"
- to={getComponentDrilldownUrl({
- componentKey: component.key,
- metric: 'Security',
- branchLike
- })}>
- <BubblesIcon size={14} />
- </Link>
- </div>
- </div>
+ <DateFromNow date={historyStartDate}>
+ {fromNow => (
+ <span className="overview-domain-timeline-date">
+ {translateWithParameters('overview.started_x', fromNow)}
+ </span>
+ )}
+ </DateFromNow>
);
}
+ renderTimeline(range: string, historyStartDate?: Date) {
+ return this.props.renderTimeline('bugs', range, this.renderTimelineStartDate(historyStartDate));
+ }
+
renderLeak() {
const { branchLike, component, leakPeriod } = this.props;
if (!leakPeriod) {
@@ -81,7 +72,7 @@ export class BugsAndVulnerabilities extends React.PureComponent<ComposedProps> {
<div className="overview-domain-measures">
<div className="overview-domain-measure">
<div className="overview-domain-measure-value">
- <span style={{ marginLeft: 30 }}>{this.props.renderIssues('new_bugs', 'BUG')}</span>
+ <span className="offset-left">{this.props.renderIssues('new_bugs', 'BUG')}</span>
{this.props.renderRating('new_reliability_rating')}
</div>
<div className="overview-domain-measure-label">
@@ -89,19 +80,8 @@ export class BugsAndVulnerabilities extends React.PureComponent<ComposedProps> {
{getMetricName('new_bugs')}
</div>
</div>
- <div className="overview-domain-measure">
- <div className="overview-domain-measure-value">
- <span style={{ marginLeft: 30 }}>
- {this.props.renderIssues('new_vulnerabilities', 'VULNERABILITY')}
- </span>
- {this.props.renderRating('new_security_rating')}
- </div>
- <div className="overview-domain-measure-label">
- <VulnerabilityIcon className="little-spacer-right" />
- {getMetricName('new_vulnerabilities')}
- </div>
- </div>
</div>
+ {this.renderTimeline('after')}
</div>
);
}
@@ -112,27 +92,21 @@ export class BugsAndVulnerabilities extends React.PureComponent<ComposedProps> {
<div className="overview-domain-measures">
<div className="overview-domain-measure">
<div className="overview-domain-measure-value">
- {this.props.renderIssues('bugs', 'BUG')}
+ <span className="offset-left">{this.props.renderIssues('bugs', 'BUG')}</span>
{this.props.renderRating('reliability_rating')}
</div>
- <div className="overview-domain-measure-label">
+ <div className="overview-domain-measure-label display-flex-center display-flex-justify-center">
<BugIcon className="little-spacer-right " />
{getMetricName('bugs')}
- {this.props.renderHistoryLink('bugs')}
- </div>
- </div>
- <div className="overview-domain-measure">
- <div className="overview-domain-measure-value">
- {this.props.renderIssues('vulnerabilities', 'VULNERABILITY')}
- {this.props.renderRating('security_rating')}
- </div>
- <div className="overview-domain-measure-label">
- <VulnerabilityIcon className="little-spacer-right " />
- {getMetricName('vulnerabilities')}
- {this.props.renderHistoryLink('vulnerabilities')}
+ <DocTooltip
+ className="little-spacer-left"
+ doc={import(/* webpackMode: "eager" */ 'Docs/tooltips/metrics/bugs.md')}
+ />
</div>
+ {this.props.renderHistoryLink('bugs')}
</div>
</div>
+ {this.renderTimeline('before', this.props.historyStartDate)}
</div>
);
}
@@ -144,7 +118,7 @@ export class BugsAndVulnerabilities extends React.PureComponent<ComposedProps> {
return null;
}
return (
- <div className="overview-card overview-card-special">
+ <div className="overview-card">
{this.renderHeader()}
<div className="overview-domain-panel">
@@ -156,4 +130,4 @@ export class BugsAndVulnerabilities extends React.PureComponent<ComposedProps> {
}
}
-export default enhance(BugsAndVulnerabilities);
+export default enhance(Bugs);
diff --git a/server/sonar-web/src/main/js/apps/overview/main/CodeSmells.tsx b/server/sonar-web/src/main/js/apps/overview/main/CodeSmells.tsx
index e67b35a42ee..fcf6d78cc6d 100644
--- a/server/sonar-web/src/main/js/apps/overview/main/CodeSmells.tsx
+++ b/server/sonar-web/src/main/js/apps/overview/main/CodeSmells.tsx
@@ -19,16 +19,15 @@
*/
import * as React from 'react';
import enhance, { ComposedProps } from './enhance';
-import DateFromNow from '../../../components/intl/DateFromNow';
+import DocTooltip from '../../../components/docs/DocTooltip';
import { getMetricName } from '../utils';
-import { translate, translateWithParameters } from '../../../helpers/l10n';
import { formatMeasure } from '../../../helpers/measures';
import CodeSmellIcon from '../../../components/icons-components/CodeSmellIcon';
import DrilldownLink from '../../../components/shared/DrilldownLink';
export class CodeSmells extends React.PureComponent<ComposedProps> {
renderHeader() {
- return this.props.renderHeader('Maintainability', translate('metric.code_smells.name'));
+ return this.props.renderHeader('Maintainability');
}
renderDebt(metric: string) {
@@ -43,27 +42,8 @@ export class CodeSmells extends React.PureComponent<ComposedProps> {
);
}
- renderTimelineStartDate() {
- if (!this.props.historyStartDate) {
- return null;
- }
- return (
- <DateFromNow date={this.props.historyStartDate}>
- {fromNow => (
- <span className="overview-domain-timeline-date">
- {translateWithParameters('overview.started_x', fromNow)}
- </span>
- )}
- </DateFromNow>
- );
- }
-
- renderTimeline(range: string, displayDate?: boolean) {
- return this.props.renderTimeline(
- 'sqale_index',
- range,
- displayDate ? this.renderTimelineStartDate() : null
- );
+ renderTimeline(range: string) {
+ return this.props.renderTimeline('sqale_index', range);
}
renderLeak() {
@@ -77,7 +57,7 @@ export class CodeSmells extends React.PureComponent<ComposedProps> {
<div className="overview-domain-measures">
<div className="overview-domain-measure">
<div className="overview-domain-measure-value">
- <span style={{ marginLeft: 30 }}>{this.renderDebt('new_technical_debt')}</span>
+ <span className="offset-left">{this.renderDebt('new_technical_debt')}</span>
{this.props.renderRating('new_maintainability_rating')}
</div>
<div className="overview-domain-measure-label">{getMetricName('new_effort')}</div>
@@ -92,7 +72,6 @@ export class CodeSmells extends React.PureComponent<ComposedProps> {
</div>
</div>
</div>
-
{this.renderTimeline('after')}
</div>
);
@@ -104,27 +83,34 @@ export class CodeSmells extends React.PureComponent<ComposedProps> {
<div className="overview-domain-measures">
<div className="overview-domain-measure">
<div className="overview-domain-measure-value">
- {this.renderDebt('sqale_index')}
+ <span className="offset-left">{this.renderDebt('sqale_index')}</span>
{this.props.renderRating('sqale_rating')}
</div>
- <div className="overview-domain-measure-label">
+ <div className="overview-domain-measure-label display-flex-center display-flex-justify-center">
{getMetricName('effort')}
- {this.props.renderHistoryLink('sqale_index')}
+ <DocTooltip
+ className="little-spacer-left"
+ doc={import(/* webpackMode: "eager" */ 'Docs/tooltips/metrics/debt.md')}
+ />
</div>
+ {this.props.renderHistoryLink('sqale_index')}
</div>
<div className="overview-domain-measure">
<div className="overview-domain-measure-value">
{this.props.renderIssues('code_smells', 'CODE_SMELL')}
</div>
- <div className="overview-domain-measure-label offset-left">
+ <div className="overview-domain-measure-label display-flex-center display-flex-justify-center">
<CodeSmellIcon className="little-spacer-right " />
{getMetricName('code_smells')}
- {this.props.renderHistoryLink('code_smells')}
+ <DocTooltip
+ className="little-spacer-left"
+ doc={import(/* webpackMode: "eager" */ 'Docs/tooltips/metrics/code-smells.md')}
+ />
</div>
+ {this.props.renderHistoryLink('code_smells')}
</div>
</div>
-
- {this.renderTimeline('before', true)}
+ {this.renderTimeline('before')}
</div>
);
}
diff --git a/server/sonar-web/src/main/js/apps/overview/main/Coverage.tsx b/server/sonar-web/src/main/js/apps/overview/main/Coverage.tsx
index 3b1960b5b31..a335fa06f50 100644
--- a/server/sonar-web/src/main/js/apps/overview/main/Coverage.tsx
+++ b/server/sonar-web/src/main/js/apps/overview/main/Coverage.tsx
@@ -19,6 +19,7 @@
*/
import * as React from 'react';
import enhance, { ComposedProps } from './enhance';
+import DocTooltip from '../../../components/docs/DocTooltip';
import DrilldownLink from '../../../components/shared/DrilldownLink';
import { getMetricName } from '../utils';
import { formatMeasure, getPeriodValue } from '../../../helpers/measures';
@@ -40,7 +41,13 @@ export class Coverage extends React.PureComponent<ComposedProps> {
}
renderTests() {
- return this.props.renderMeasure('tests');
+ return this.props.renderMeasure(
+ 'tests',
+ <DocTooltip
+ className="little-spacer-left"
+ doc={import(/* webpackMode: "eager" */ 'Docs/tooltips/metrics/unit-tests.md')}
+ />
+ );
}
renderCoverage() {
@@ -50,7 +57,7 @@ export class Coverage extends React.PureComponent<ComposedProps> {
return (
<div className="overview-domain-measure">
- <div className="display-inline-block text-middle big-spacer-right">
+ <div className="display-inline-block text-middle big-spacer-right neg-offset-left">
<CoverageRating size="big" value={coverage} />
</div>
@@ -63,11 +70,16 @@ export class Coverage extends React.PureComponent<ComposedProps> {
</DrilldownLink>
</div>
- <div className="overview-domain-measure-label">
+ <div className="overview-domain-measure-label display-flex-center display-flex-justify-center">
{getMetricName('coverage')}
- {this.props.renderHistoryLink('coverage')}
+ <DocTooltip
+ className="little-spacer-left"
+ doc={import(/* webpackMode: "eager" */ 'Docs/tooltips/metrics/coverage.md')}
+ />
</div>
+ {this.props.renderHistoryLink('coverage')}
</div>
+ {this.props.renderHistoryLink('coverage')}
</div>
);
}
@@ -117,7 +129,9 @@ export class Coverage extends React.PureComponent<ComposedProps> {
{getMetricName('new_lines_to_cover')}
</div>
) : (
- <div className="overview-domain-measure-label">{getMetricName('new_coverage')}</div>
+ <div className="overview-domain-measure-label display-flex-center display-flex-justify-center">
+ {getMetricName('new_coverage')}
+ </div>
);
return (
@@ -131,7 +145,7 @@ export class Coverage extends React.PureComponent<ComposedProps> {
renderNutshell() {
return (
<div className="overview-domain-nutshell">
- <div className="overview-domain-measures">
+ <div className="overview-domain-measures overview-domain-measures-big">
{this.renderCoverage()}
{this.renderTests()}
</div>
diff --git a/server/sonar-web/src/main/js/apps/overview/main/Duplications.tsx b/server/sonar-web/src/main/js/apps/overview/main/Duplications.tsx
index 04dea37a24e..30b4c08d570 100644
--- a/server/sonar-web/src/main/js/apps/overview/main/Duplications.tsx
+++ b/server/sonar-web/src/main/js/apps/overview/main/Duplications.tsx
@@ -19,6 +19,7 @@
*/
import * as React from 'react';
import enhance, { ComposedProps } from './enhance';
+import DocTooltip from '../../../components/docs/DocTooltip';
import DrilldownLink from '../../../components/shared/DrilldownLink';
import { getMetricName } from '../utils';
import { formatMeasure, getPeriodValue } from '../../../helpers/measures';
@@ -35,7 +36,13 @@ export class Duplications extends React.PureComponent<ComposedProps> {
}
renderDuplicatedBlocks() {
- return this.props.renderMeasure('duplicated_blocks');
+ return this.props.renderMeasure(
+ 'duplicated_blocks',
+ <DocTooltip
+ className="little-spacer-left"
+ doc={import(/* webpackMode: "eager" */ 'Docs/tooltips/metrics/duplicated-blocks.md')}
+ />
+ );
}
renderDuplications() {
@@ -49,7 +56,7 @@ export class Duplications extends React.PureComponent<ComposedProps> {
return (
<div className="overview-domain-measure">
- <div className="display-inline-block text-middle big-spacer-right">
+ <div className="display-inline-block text-middle big-spacer-right neg-offset-left">
<DuplicationsRating size="big" value={duplications} />
</div>
@@ -63,10 +70,14 @@ export class Duplications extends React.PureComponent<ComposedProps> {
</DrilldownLink>
</div>
- <div className="overview-domain-measure-label offset-left">
+ <div className="overview-domain-measure-label display-flex-center display-flex-justify-center">
{getMetricName('duplications')}
- {this.props.renderHistoryLink('duplicated_lines_density')}
+ <DocTooltip
+ className="little-spacer-left"
+ doc={import(/* webpackMode: "eager" */ 'Docs/tooltips/metrics/duplications.md')}
+ />
</div>
+ {this.props.renderHistoryLink('duplicated_lines_density')}
</div>
</div>
);
@@ -131,7 +142,7 @@ export class Duplications extends React.PureComponent<ComposedProps> {
renderNutshell() {
return (
<div className="overview-domain-nutshell">
- <div className="overview-domain-measures">
+ <div className="overview-domain-measures overview-domain-measures-big">
{this.renderDuplications()}
{this.renderDuplicatedBlocks()}
</div>
diff --git a/server/sonar-web/src/main/js/apps/overview/main/VulnerabilitiesAndHotspots.tsx b/server/sonar-web/src/main/js/apps/overview/main/VulnerabilitiesAndHotspots.tsx
new file mode 100644
index 00000000000..3fd1e96db20
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/overview/main/VulnerabilitiesAndHotspots.tsx
@@ -0,0 +1,127 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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 enhance, { ComposedProps } from './enhance';
+import DocTooltip from '../../../components/docs/DocTooltip';
+import VulnerabilityIcon from '../../../components/icons-components/VulnerabilityIcon';
+import { getMetricName } from '../utils';
+import SecurityHotspotIcon from '../../../components/icons-components/SecurityHotspotIcon';
+
+export class VulnerabiltiesAndHotspots extends React.PureComponent<ComposedProps> {
+ renderHeader() {
+ return this.props.renderHeader('Security');
+ }
+
+ renderTimeline(range: string) {
+ return this.props.renderTimeline('vulnerabilities', range);
+ }
+
+ renderLeak() {
+ const { leakPeriod } = this.props;
+ if (!leakPeriod) {
+ return null;
+ }
+
+ return (
+ <div className="overview-domain-leak">
+ <div className="overview-domain-measures">
+ <div className="overview-domain-measure">
+ <div className="overview-domain-measure-value">
+ <span className="offset-left">
+ {this.props.renderIssues('new_vulnerabilities', 'VULNERABILITY')}
+ </span>
+ {this.props.renderRating('new_security_rating')}
+ </div>
+ <div className="overview-domain-measure-label">
+ <VulnerabilityIcon className="little-spacer-right" />
+ {getMetricName('new_vulnerabilities')}
+ </div>
+ </div>
+ <div className="overview-domain-measure">
+ <div className="overview-domain-measure-value overview-domain-measure-value-small">
+ <span>{this.props.renderIssues('new_security_hotspots', 'SECURITY_HOTSPOT')}</span>
+ </div>
+ <div className="overview-domain-measure-label">
+ <SecurityHotspotIcon className="little-spacer-right" />
+ {getMetricName('new_security_hotspots')}
+ </div>
+ </div>
+ </div>
+ {this.renderTimeline('after')}
+ </div>
+ );
+ }
+
+ renderNutshell() {
+ return (
+ <div className="overview-domain-nutshell">
+ <div className="overview-domain-measures">
+ <div className="overview-domain-measure">
+ <div className="overview-domain-measure-value">
+ <span className="offset-left">
+ {this.props.renderIssues('vulnerabilities', 'VULNERABILITY')}
+ </span>
+ {this.props.renderRating('security_rating')}
+ </div>
+ <div className="overview-domain-measure-label display-flex-center display-flex-justify-center">
+ <VulnerabilityIcon className="little-spacer-right" />
+ {getMetricName('vulnerabilities')}
+ <DocTooltip
+ className="little-spacer-left"
+ doc={import(/* webpackMode: "eager" */ 'Docs/tooltips/metrics/vulnerabilities.md')}
+ />
+ </div>
+ {this.props.renderHistoryLink('vulnerabilities')}
+ </div>
+ <div className="overview-domain-measure">
+ <div className="overview-domain-measure-value overview-domain-measure-value-small">
+ {this.props.renderIssues('security_hotspots', 'SECURITY_HOTSPOT')}
+ </div>
+ <div className="overview-domain-measure-label display-flex-center display-flex-justify-center">
+ <SecurityHotspotIcon className="little-spacer-right" />
+ {getMetricName('security_hotspots')}
+ <DocTooltip
+ className="little-spacer-left"
+ doc={import(/* webpackMode: "eager" */ 'Docs/tooltips/metrics/security-hotspots.md')}
+ />
+ </div>
+ {this.props.renderHistoryLink('security_hotspots')}
+ </div>
+ </div>
+ {this.renderTimeline('before')}
+ </div>
+ );
+ }
+
+ render() {
+ return (
+ <div className="overview-card">
+ {this.renderHeader()}
+
+ <div className="overview-domain-panel">
+ {this.renderNutshell()}
+ {this.renderLeak()}
+ </div>
+ </div>
+ );
+ }
+}
+
+export default enhance(VulnerabiltiesAndHotspots);
diff --git a/server/sonar-web/src/main/js/apps/overview/main/__tests__/Bugs-test.tsx b/server/sonar-web/src/main/js/apps/overview/main/__tests__/Bugs-test.tsx
new file mode 100644
index 00000000000..1c52153ed50
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/overview/main/__tests__/Bugs-test.tsx
@@ -0,0 +1,87 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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 { IntlProvider } from 'react-intl';
+import { render } from 'enzyme';
+import Bugs from '../Bugs';
+import { ComposedProps } from '../enhance';
+import {
+ mockComponent,
+ mockMainBranch,
+ mockMeasureEnhanced,
+ mockMetric
+} from '../../../../helpers/testMocks';
+
+it('should render correctly', () => {
+ expect(shallowRender()).toMatchSnapshot();
+});
+
+function shallowRender(props: Partial<ComposedProps> = {}) {
+ return render(
+ <IntlProvider locale="en" messages={{}}>
+ <Bugs
+ branchLike={mockMainBranch()}
+ component={mockComponent()}
+ history={{ bugs: [] }}
+ historyStartDate={new Date('2019-01-14T15:44:51.000Z')}
+ leakPeriod={{ index: 1, mode: 'days' } as T.Period}
+ measures={[
+ mockMeasureEnhanced({
+ metric: mockMetric({
+ id: 'bugs',
+ key: 'bugs',
+ name: 'Bugs',
+ type: 'INT'
+ }),
+ value: '5'
+ }),
+ mockMeasureEnhanced({
+ metric: mockMetric({
+ id: 'new_bugs',
+ key: 'new_bugs',
+ name: 'New Bugs',
+ type: 'INT'
+ }),
+ value: '2'
+ }),
+ mockMeasureEnhanced({
+ metric: mockMetric({
+ id: 'reliability_rating',
+ key: 'reliability_rating',
+ name: 'Reliability',
+ type: 'RATING'
+ }),
+ value: '1.0'
+ }),
+ mockMeasureEnhanced({
+ metric: mockMetric({
+ id: 'new_reliability_rating',
+ key: 'new_reliability_rating',
+ name: 'New Reliability',
+ type: 'RATING'
+ }),
+ value: '2.0'
+ })
+ ]}
+ {...props}
+ />
+ </IntlProvider>
+ );
+}
diff --git a/server/sonar-web/src/main/js/apps/overview/main/__tests__/CodeSmells-test.tsx b/server/sonar-web/src/main/js/apps/overview/main/__tests__/CodeSmells-test.tsx
new file mode 100644
index 00000000000..eb4889945ed
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/overview/main/__tests__/CodeSmells-test.tsx
@@ -0,0 +1,113 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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 { render } from 'enzyme';
+import CodeSmells from '../CodeSmells';
+import { ComposedProps } from '../enhance';
+import {
+ mockComponent,
+ mockMainBranch,
+ mockMeasureEnhanced,
+ mockMetric
+} from '../../../../helpers/testMocks';
+
+it('should render correctly', () => {
+ expect(shallowRender()).toMatchSnapshot();
+});
+
+function shallowRender(props: Partial<ComposedProps> = {}) {
+ return render(
+ <CodeSmells
+ branchLike={mockMainBranch()}
+ component={mockComponent()}
+ history={{ sqale_index: [] }}
+ leakPeriod={{ index: 1 } as T.Period}
+ measures={[
+ mockMeasureEnhanced({
+ metric: mockMetric({
+ id: 'code_smells',
+ key: 'code_smells',
+ name: 'Code Smells',
+ type: 'INT'
+ }),
+ value: '15'
+ }),
+ mockMeasureEnhanced({
+ metric: mockMetric({
+ id: 'sqale_index',
+ key: 'sqale_index',
+ name: 'Debt',
+ type: 'INT'
+ }),
+ value: '1052'
+ }),
+ mockMeasureEnhanced({
+ metric: mockMetric({
+ id: 'sqale_rating',
+ key: 'sqale_rating',
+ name: 'Maintainability',
+ type: 'RATING'
+ }),
+ value: '2.0'
+ }),
+ mockMeasureEnhanced({
+ metric: mockMetric({
+ id: 'new_code_smells',
+ key: 'new_code_smells',
+ name: 'New Code Smells',
+ type: 'INT'
+ }),
+ periods: [
+ {
+ bestValue: true,
+ index: 1,
+ value: '52'
+ }
+ ]
+ }),
+ mockMeasureEnhanced({
+ metric: mockMetric({
+ id: 'new_technical_debt',
+ key: 'new_technical_debt',
+ name: 'New Debt',
+ type: 'INT'
+ }),
+ periods: [
+ {
+ bestValue: true,
+ index: 1,
+ value: '85'
+ }
+ ]
+ }),
+ mockMeasureEnhanced({
+ metric: mockMetric({
+ id: 'new_maintainability_rating',
+ key: 'new_maintainability_rating',
+ name: 'New Maintainability',
+ type: 'RATING'
+ }),
+ value: '3.0'
+ })
+ ]}
+ {...props}
+ />
+ );
+}
diff --git a/server/sonar-web/src/main/js/apps/overview/main/__tests__/Coverage-test.tsx b/server/sonar-web/src/main/js/apps/overview/main/__tests__/Coverage-test.tsx
new file mode 100644
index 00000000000..6186947ffda
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/overview/main/__tests__/Coverage-test.tsx
@@ -0,0 +1,73 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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 { render } from 'enzyme';
+import Coverage from '../Coverage';
+import { ComposedProps } from '../enhance';
+import {
+ mockComponent,
+ mockMainBranch,
+ mockMeasureEnhanced,
+ mockMetric
+} from '../../../../helpers/testMocks';
+
+it('should render correctly', () => {
+ expect(shallowRender()).toMatchSnapshot();
+});
+
+function shallowRender(props: Partial<ComposedProps> = {}) {
+ return render(
+ <Coverage
+ branchLike={mockMainBranch()}
+ component={mockComponent()}
+ leakPeriod={{ index: 1 } as T.Period}
+ measures={[
+ mockMeasureEnhanced(),
+ mockMeasureEnhanced({
+ metric: mockMetric({
+ id: 'new_lines_to_cover',
+ key: 'new_lines_to_cover',
+ name: 'New Lines to Cover',
+ type: 'INT'
+ }),
+ value: '52'
+ }),
+ mockMeasureEnhanced({
+ metric: mockMetric({
+ id: 'new_coverage',
+ key: 'new_coverage',
+ name: 'New Coverage'
+ }),
+ value: '85.0'
+ }),
+ mockMeasureEnhanced({
+ metric: mockMetric({
+ id: 'tests',
+ key: 'tests',
+ name: 'Unit Tests',
+ type: 'INT'
+ }),
+ value: '15'
+ })
+ ]}
+ {...props}
+ />
+ );
+}
diff --git a/server/sonar-web/src/main/js/apps/overview/main/__tests__/Duplications-test.tsx b/server/sonar-web/src/main/js/apps/overview/main/__tests__/Duplications-test.tsx
new file mode 100644
index 00000000000..e98f2c2cfd3
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/overview/main/__tests__/Duplications-test.tsx
@@ -0,0 +1,92 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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 { render } from 'enzyme';
+import Duplications from '../Duplications';
+import { ComposedProps } from '../enhance';
+import {
+ mockComponent,
+ mockMainBranch,
+ mockMeasureEnhanced,
+ mockMetric
+} from '../../../../helpers/testMocks';
+
+it('should render correctly', () => {
+ expect(shallowRender()).toMatchSnapshot();
+});
+
+function shallowRender(props: Partial<ComposedProps> = {}) {
+ return render(
+ <Duplications
+ branchLike={mockMainBranch()}
+ component={mockComponent()}
+ leakPeriod={{ index: 1 } as T.Period}
+ measures={[
+ mockMeasureEnhanced({
+ metric: mockMetric({
+ id: 'duplicated_lines_density',
+ key: 'duplicated_lines_density',
+ name: 'Duplicated Lines'
+ }),
+ value: '0.5'
+ }),
+ mockMeasureEnhanced({
+ metric: mockMetric({
+ id: 'new_lines',
+ key: 'new_lines',
+ name: 'New Lines',
+ type: 'INT'
+ }),
+ value: '52'
+ }),
+ mockMeasureEnhanced({
+ metric: mockMetric({
+ id: 'new_duplicated_lines_density',
+ key: 'new_duplicated_lines_density',
+ name: 'New Duplicated Lines'
+ }),
+ periods: [
+ {
+ bestValue: true,
+ index: 1,
+ value: '1.5'
+ }
+ ]
+ }),
+ mockMeasureEnhanced({
+ metric: mockMetric({
+ id: 'duplicated_blocks',
+ key: 'duplicated_blocks',
+ name: 'Duplicated Blocks',
+ type: 'INT'
+ }),
+ periods: [
+ {
+ bestValue: true,
+ index: 1,
+ value: '2'
+ }
+ ]
+ })
+ ]}
+ {...props}
+ />
+ );
+}
diff --git a/server/sonar-web/src/main/js/apps/overview/main/__tests__/VulnerabilitiesAndHotspots-test.tsx b/server/sonar-web/src/main/js/apps/overview/main/__tests__/VulnerabilitiesAndHotspots-test.tsx
new file mode 100644
index 00000000000..b9b05f5ad58
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/overview/main/__tests__/VulnerabilitiesAndHotspots-test.tsx
@@ -0,0 +1,119 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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 { render } from 'enzyme';
+import VulnerabilitiesAndHotspots from '../VulnerabilitiesAndHotspots';
+import { ComposedProps } from '../enhance';
+import {
+ mockComponent,
+ mockMainBranch,
+ mockMeasureEnhanced,
+ mockMetric
+} from '../../../../helpers/testMocks';
+
+it('should render correctly', () => {
+ expect(shallowRender()).toMatchSnapshot();
+});
+
+function shallowRender(props: Partial<ComposedProps> = {}) {
+ return render(
+ <VulnerabilitiesAndHotspots
+ branchLike={mockMainBranch()}
+ component={mockComponent()}
+ history={{ vulnerabilities: [] }}
+ leakPeriod={{ index: 1 } as T.Period}
+ measures={[
+ mockMeasureEnhanced({
+ metric: mockMetric({
+ id: 'vulnerabilities',
+ key: 'vulnerabilities',
+ name: 'Vulnerabilities',
+ type: 'INT'
+ }),
+ value: '0'
+ }),
+ mockMeasureEnhanced({
+ metric: mockMetric({
+ id: 'security_hotspots',
+ key: 'security_hotspots',
+ name: 'Security Hotspots',
+ type: 'INT'
+ }),
+ value: '0'
+ }),
+ mockMeasureEnhanced({
+ metric: mockMetric({
+ id: 'security_rating',
+ key: 'security_rating',
+ name: 'Security',
+ type: 'RATING'
+ }),
+ value: '1.0'
+ }),
+ mockMeasureEnhanced({
+ metric: mockMetric({
+ id: 'new_vulnerabilities',
+ key: 'new_vulnerabilities',
+ name: 'New Vulnerabilities',
+ type: 'INT'
+ }),
+ periods: [
+ {
+ bestValue: true,
+ index: 1,
+ value: '1'
+ }
+ ]
+ }),
+ mockMeasureEnhanced({
+ metric: mockMetric({
+ id: 'new_security_hotspots',
+ key: 'new_security_hotspots',
+ name: 'New Security Hotspots',
+ type: 'INT'
+ }),
+ periods: [
+ {
+ bestValue: true,
+ index: 1,
+ value: '10'
+ }
+ ]
+ }),
+ mockMeasureEnhanced({
+ metric: mockMetric({
+ id: 'new_security_rating',
+ key: 'new_security_rating',
+ name: 'New Security Rating',
+ type: 'RATING'
+ }),
+ periods: [
+ {
+ bestValue: true,
+ index: 1,
+ value: '5.0'
+ }
+ ]
+ })
+ ]}
+ {...props}
+ />
+ );
+}
diff --git a/server/sonar-web/src/main/js/apps/overview/main/__tests__/__snapshots__/Bugs-test.tsx.snap b/server/sonar-web/src/main/js/apps/overview/main/__tests__/__snapshots__/Bugs-test.tsx.snap
new file mode 100644
index 00000000000..95c23ca9f85
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/overview/main/__tests__/__snapshots__/Bugs-test.tsx.snap
@@ -0,0 +1,180 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render correctly 1`] = `
+<div
+ class="overview-card"
+>
+ <div
+ class="overview-card-header"
+ >
+ <div
+ class="overview-title"
+ >
+ <span>
+ metric_domain.Reliability
+ </span>
+ <a
+ class="spacer-left small"
+ >
+ layout.measures
+ </a>
+ </div>
+ </div>
+ <div
+ class="overview-domain-panel"
+ >
+ <div
+ class="overview-domain-nutshell"
+ >
+ <div
+ class="overview-domain-measures"
+ >
+ <div
+ class="overview-domain-measure"
+ >
+ <div
+ class="overview-domain-measure-value"
+ >
+ <span
+ class="offset-left"
+ >
+ <a>
+ 5
+ </a>
+ </span>
+ <div
+ class="overview-domain-measure-sup"
+ >
+ <a
+ class="link-no-underline"
+ >
+ <span
+ aria-label="metric.has_rating_X.A"
+ class="rating rating-A"
+ >
+ A
+ </span>
+ </a>
+ </div>
+ </div>
+ <div
+ class="overview-domain-measure-label display-flex-center display-flex-justify-center"
+ >
+ <svg
+ class="little-spacer-right "
+ height="16"
+ space="preserve"
+ style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421"
+ version="1.1"
+ viewBox="0 0 16 16"
+ width="16"
+ xlink="http://www.w3.org/1999/xlink"
+ >
+ <path
+ d="M11 9h1.3l.5.8.8-.5-.8-1.3H11v-.3l2-2.3V3h-1v2l-1 1.2V5c-.1-.8-.7-1.5-1.4-1.9L11 1.8l-.7-.7-1.8 1.6-1.8-1.6-.7.7 1.5 1.3C6.7 3.5 6.1 4.2 6 5v1.1L5 5V3H4v2.3l2 2.3V8H4.2l-.7 1.2.8.5.4-.7H6v.3l-2 1.9V14h1v-2.4l1-1C6 12 7.1 13 8.4 13h.8c.7 0 1.4-.3 1.8-.9.3-.4.3-.9.2-1.4l.9.9V14h1v-2.8l-2-1.9V9zm-2 2H8V6h1v5z"
+ style="fill:currentColor"
+ />
+ </svg>
+ overview.metric.bugs
+ </div>
+ <a
+ class="overview-domain-measure-history-link"
+ >
+ <svg
+ height="16"
+ space="preserve"
+ style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421"
+ version="1.1"
+ viewBox="0 0 16 16"
+ width="16"
+ xlink="http://www.w3.org/1999/xlink"
+ >
+ <path
+ d="M14.7 3.4v3.3c0 .1 0 .2-.1.2s-.2 0-.3-.1l-.9-.9-4.8 4.8c-.1.1-.1.1-.2.1s-.1 0-.2-.1L6.4 9l-3.2 3.2-1.5-1.5 4.5-4.5c.1-.1.1-.1.2-.1s.1 0 .2.1L8.4 8l3.5-3.5-.9-1c-.1-.1-.1-.2-.1-.3s.1-.1.2-.1h3.3c.1 0 .1 0 .2.1.1 0 .1.1.1.2z"
+ style="fill:currentColor"
+ />
+ </svg>
+ <span>
+ project_activity.page
+ </span>
+ </a>
+ </div>
+ </div>
+ <div
+ class="overview-domain-timeline"
+ >
+ <span
+ class="overview-domain-timeline-date"
+ >
+ overview.started_x.3 months ago
+ </span>
+ </div>
+ </div>
+ <div
+ class="overview-domain-leak"
+ >
+ <div
+ class="overview-legend overview-legend-spaced-line"
+ >
+ overview.new_code_period_x.overview.period.days.
+ </div>
+ <div
+ class="overview-domain-measures"
+ >
+ <div
+ class="overview-domain-measure"
+ >
+ <div
+ class="overview-domain-measure-value"
+ >
+ <span
+ class="offset-left"
+ >
+ <a>
+ 1
+ </a>
+ </span>
+ <div
+ class="overview-domain-measure-sup"
+ >
+ <a
+ class="link-no-underline"
+ >
+ <span
+ aria-label="metric.has_rating_X.A"
+ class="rating rating-A"
+ >
+ A
+ </span>
+ </a>
+ </div>
+ </div>
+ <div
+ class="overview-domain-measure-label"
+ >
+ <svg
+ class="little-spacer-right"
+ height="16"
+ space="preserve"
+ style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421"
+ version="1.1"
+ viewBox="0 0 16 16"
+ width="16"
+ xlink="http://www.w3.org/1999/xlink"
+ >
+ <path
+ d="M11 9h1.3l.5.8.8-.5-.8-1.3H11v-.3l2-2.3V3h-1v2l-1 1.2V5c-.1-.8-.7-1.5-1.4-1.9L11 1.8l-.7-.7-1.8 1.6-1.8-1.6-.7.7 1.5 1.3C6.7 3.5 6.1 4.2 6 5v1.1L5 5V3H4v2.3l2 2.3V8H4.2l-.7 1.2.8.5.4-.7H6v.3l-2 1.9V14h1v-2.4l1-1C6 12 7.1 13 8.4 13h.8c.7 0 1.4-.3 1.8-.9.3-.4.3-.9.2-1.4l.9.9V14h1v-2.8l-2-1.9V9zm-2 2H8V6h1v5z"
+ style="fill:currentColor"
+ />
+ </svg>
+ overview.metric.new_bugs
+ </div>
+ </div>
+ </div>
+ <div
+ class="overview-domain-timeline"
+ />
+ </div>
+ </div>
+</div>
+`;
diff --git a/server/sonar-web/src/main/js/apps/overview/main/__tests__/__snapshots__/CodeSmells-test.tsx.snap b/server/sonar-web/src/main/js/apps/overview/main/__tests__/__snapshots__/CodeSmells-test.tsx.snap
new file mode 100644
index 00000000000..1842d0aa2b8
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/overview/main/__tests__/__snapshots__/CodeSmells-test.tsx.snap
@@ -0,0 +1,223 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render correctly 1`] = `
+<div
+ class="overview-card"
+ id="overview-code-smells"
+>
+ <div
+ class="overview-card-header"
+ >
+ <div
+ class="overview-title"
+ >
+ <span>
+ metric_domain.Maintainability
+ </span>
+ <a
+ class="spacer-left small"
+ >
+ layout.measures
+ </a>
+ </div>
+ </div>
+ <div
+ class="overview-domain-panel"
+ >
+ <div
+ class="overview-domain-nutshell"
+ >
+ <div
+ class="overview-domain-measures"
+ >
+ <div
+ class="overview-domain-measure"
+ >
+ <div
+ class="overview-domain-measure-value"
+ >
+ <span
+ class="offset-left"
+ >
+ <a>
+ work_duration.x_days.2
+ </a>
+ </span>
+ <div
+ class="overview-domain-measure-sup"
+ >
+ <a
+ class="link-no-underline"
+ >
+ <span
+ aria-label="metric.has_rating_X.B"
+ class="rating rating-B"
+ >
+ B
+ </span>
+ </a>
+ </div>
+ </div>
+ <div
+ class="overview-domain-measure-label display-flex-center display-flex-justify-center"
+ >
+ overview.metric.effort
+ </div>
+ <a
+ class="overview-domain-measure-history-link"
+ >
+ <svg
+ height="16"
+ space="preserve"
+ style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421"
+ version="1.1"
+ viewBox="0 0 16 16"
+ width="16"
+ xlink="http://www.w3.org/1999/xlink"
+ >
+ <path
+ d="M14.7 3.4v3.3c0 .1 0 .2-.1.2s-.2 0-.3-.1l-.9-.9-4.8 4.8c-.1.1-.1.1-.2.1s-.1 0-.2-.1L6.4 9l-3.2 3.2-1.5-1.5 4.5-4.5c.1-.1.1-.1.2-.1s.1 0 .2.1L8.4 8l3.5-3.5-.9-1c-.1-.1-.1-.2-.1-.3s.1-.1.2-.1h3.3c.1 0 .1 0 .2.1.1 0 .1.1.1.2z"
+ style="fill:currentColor"
+ />
+ </svg>
+ <span>
+ project_activity.page
+ </span>
+ </a>
+ </div>
+ <div
+ class="overview-domain-measure"
+ >
+ <div
+ class="overview-domain-measure-value"
+ >
+ <a>
+ 15
+ </a>
+ </div>
+ <div
+ class="overview-domain-measure-label display-flex-center display-flex-justify-center"
+ >
+ <svg
+ class="little-spacer-right "
+ height="16"
+ space="preserve"
+ style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421"
+ version="1.1"
+ viewBox="0 0 16 16"
+ width="16"
+ xlink="http://www.w3.org/1999/xlink"
+ >
+ <path
+ d="M8 2C4.7 2 2 4.7 2 8s2.7 6 6 6 6-2.7 6-6-2.7-6-6-6zm-.5 5.5h.9v.9h-.9v-.9zm-3.8.2c-.1 0-.2-.1-.2-.2 0-.4.1-1.2.6-2S5.3 4.2 5.6 4c.2 0 .3 0 .3.1l1.3 2.3c0 .1 0 .2-.1.2-.1.2-.2.3-.3.5-.1.2-.2.4-.2.5 0 .1-.1.2-.2.2l-2.7-.1zM9.9 12c-.3.2-1.1.5-2 .5-.9 0-1.7-.3-2-.5-.1 0-.1-.2-.1-.3l1.3-2.3c0-.1.1-.1.2-.1.2.1.3.1.5.1s.4 0 .5-.1c.1 0 .2 0 .2.1l1.3 2.3c.2.2.2.3.1.3zm2.5-4.1L9.7 8c-.1 0-.2-.1-.2-.2 0-.2-.1-.4-.2-.5 0-.1-.2-.3-.3-.4-.1 0-.1-.1-.1-.2l1.3-2.3c.1-.1.2-.1.3-.1.3.2 1 .7 1.5 1.5s.6 1.6.6 2c0 0-.1.1-.2.1z"
+ style="fill:currentColor"
+ />
+ </svg>
+ overview.metric.code_smells
+ </div>
+ <a
+ class="overview-domain-measure-history-link"
+ >
+ <svg
+ height="16"
+ space="preserve"
+ style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421"
+ version="1.1"
+ viewBox="0 0 16 16"
+ width="16"
+ xlink="http://www.w3.org/1999/xlink"
+ >
+ <path
+ d="M14.7 3.4v3.3c0 .1 0 .2-.1.2s-.2 0-.3-.1l-.9-.9-4.8 4.8c-.1.1-.1.1-.2.1s-.1 0-.2-.1L6.4 9l-3.2 3.2-1.5-1.5 4.5-4.5c.1-.1.1-.1.2-.1s.1 0 .2.1L8.4 8l3.5-3.5-.9-1c-.1-.1-.1-.2-.1-.3s.1-.1.2-.1h3.3c.1 0 .1 0 .2.1.1 0 .1.1.1.2z"
+ style="fill:currentColor"
+ />
+ </svg>
+ <span>
+ project_activity.page
+ </span>
+ </a>
+ </div>
+ </div>
+ <div
+ class="overview-domain-timeline"
+ />
+ </div>
+ <div
+ class="overview-domain-leak"
+ >
+ <div
+ class="overview-domain-measures"
+ >
+ <div
+ class="overview-domain-measure"
+ >
+ <div
+ class="overview-domain-measure-value"
+ >
+ <span
+ class="offset-left"
+ >
+ <a>
+ work_duration.x_hours.1
+ </a>
+ </span>
+ <div
+ class="overview-domain-measure-sup"
+ >
+ <a
+ class="link-no-underline"
+ >
+ <span
+ aria-label="metric.has_rating_X.A"
+ class="rating rating-A"
+ >
+ A
+ </span>
+ </a>
+ </div>
+ </div>
+ <div
+ class="overview-domain-measure-label"
+ >
+ overview.metric.new_effort
+ </div>
+ </div>
+ <div
+ class="overview-domain-measure"
+ >
+ <div
+ class="overview-domain-measure-value"
+ >
+ <a>
+ 52
+ </a>
+ </div>
+ <div
+ class="overview-domain-measure-label"
+ >
+ <svg
+ class="little-spacer-right"
+ height="16"
+ space="preserve"
+ style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421"
+ version="1.1"
+ viewBox="0 0 16 16"
+ width="16"
+ xlink="http://www.w3.org/1999/xlink"
+ >
+ <path
+ d="M8 2C4.7 2 2 4.7 2 8s2.7 6 6 6 6-2.7 6-6-2.7-6-6-6zm-.5 5.5h.9v.9h-.9v-.9zm-3.8.2c-.1 0-.2-.1-.2-.2 0-.4.1-1.2.6-2S5.3 4.2 5.6 4c.2 0 .3 0 .3.1l1.3 2.3c0 .1 0 .2-.1.2-.1.2-.2.3-.3.5-.1.2-.2.4-.2.5 0 .1-.1.2-.2.2l-2.7-.1zM9.9 12c-.3.2-1.1.5-2 .5-.9 0-1.7-.3-2-.5-.1 0-.1-.2-.1-.3l1.3-2.3c0-.1.1-.1.2-.1.2.1.3.1.5.1s.4 0 .5-.1c.1 0 .2 0 .2.1l1.3 2.3c.2.2.2.3.1.3zm2.5-4.1L9.7 8c-.1 0-.2-.1-.2-.2 0-.2-.1-.4-.2-.5 0-.1-.2-.3-.3-.4-.1 0-.1-.1-.1-.2l1.3-2.3c.1-.1.2-.1.3-.1.3.2 1 .7 1.5 1.5s.6 1.6.6 2c0 0-.1.1-.2.1z"
+ style="fill:currentColor"
+ />
+ </svg>
+ overview.metric.new_code_smells
+ </div>
+ </div>
+ </div>
+ <div
+ class="overview-domain-timeline"
+ />
+ </div>
+ </div>
+</div>
+`;
diff --git a/server/sonar-web/src/main/js/apps/overview/main/__tests__/__snapshots__/Coverage-test.tsx.snap b/server/sonar-web/src/main/js/apps/overview/main/__tests__/__snapshots__/Coverage-test.tsx.snap
new file mode 100644
index 00000000000..65cc3f86dcb
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/overview/main/__tests__/__snapshots__/Coverage-test.tsx.snap
@@ -0,0 +1,187 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render correctly 1`] = `
+<div
+ class="overview-card"
+>
+ <div
+ class="overview-card-header"
+ >
+ <div
+ class="overview-title"
+ >
+ <span>
+ metric.coverage.name
+ </span>
+ <a
+ class="spacer-left small"
+ >
+ layout.measures
+ </a>
+ </div>
+ </div>
+ <div
+ class="overview-domain-panel"
+ >
+ <div
+ class="overview-domain-nutshell"
+ >
+ <div
+ class="overview-domain-measures overview-domain-measures-big"
+ >
+ <div
+ class="overview-domain-measure"
+ >
+ <div
+ class="display-inline-block text-middle big-spacer-right neg-offset-left"
+ />
+ <div
+ class="display-inline-block text-middle"
+ >
+ <div
+ class="overview-domain-measure-value"
+ >
+ <a>
+ <span
+ class="js-overview-main-coverage"
+ >
+ 1.0%
+ </span>
+ </a>
+ </div>
+ <div
+ class="overview-domain-measure-label display-flex-center display-flex-justify-center"
+ >
+ overview.metric.coverage
+ </div>
+ <a
+ class="overview-domain-measure-history-link"
+ >
+ <svg
+ height="16"
+ space="preserve"
+ style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421"
+ version="1.1"
+ viewBox="0 0 16 16"
+ width="16"
+ xlink="http://www.w3.org/1999/xlink"
+ >
+ <path
+ d="M14.7 3.4v3.3c0 .1 0 .2-.1.2s-.2 0-.3-.1l-.9-.9-4.8 4.8c-.1.1-.1.1-.2.1s-.1 0-.2-.1L6.4 9l-3.2 3.2-1.5-1.5 4.5-4.5c.1-.1.1-.1.2-.1s.1 0 .2.1L8.4 8l3.5-3.5-.9-1c-.1-.1-.1-.2-.1-.3s.1-.1.2-.1h3.3c.1 0 .1 0 .2.1.1 0 .1.1.1.2z"
+ style="fill:currentColor"
+ />
+ </svg>
+ <span>
+ project_activity.page
+ </span>
+ </a>
+ </div>
+ <a
+ class="overview-domain-measure-history-link"
+ >
+ <svg
+ height="16"
+ space="preserve"
+ style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421"
+ version="1.1"
+ viewBox="0 0 16 16"
+ width="16"
+ xlink="http://www.w3.org/1999/xlink"
+ >
+ <path
+ d="M14.7 3.4v3.3c0 .1 0 .2-.1.2s-.2 0-.3-.1l-.9-.9-4.8 4.8c-.1.1-.1.1-.2.1s-.1 0-.2-.1L6.4 9l-3.2 3.2-1.5-1.5 4.5-4.5c.1-.1.1-.1.2-.1s.1 0 .2.1L8.4 8l3.5-3.5-.9-1c-.1-.1-.1-.2-.1-.3s.1-.1.2-.1h3.3c.1 0 .1 0 .2.1.1 0 .1.1.1.2z"
+ style="fill:currentColor"
+ />
+ </svg>
+ <span>
+ project_activity.page
+ </span>
+ </a>
+ </div>
+ <div
+ class="overview-domain-measure"
+ >
+ <div
+ class="overview-domain-measure-value"
+ >
+ <a>
+ <span
+ class="js-overview-main-tests"
+ >
+ 15
+ </span>
+ </a>
+ </div>
+ <div
+ class="overview-domain-measure-label display-flex-center display-flex-justify-center"
+ >
+ Unit Tests
+ <a
+ class="overview-domain-measure-history-link"
+ >
+ <svg
+ height="16"
+ space="preserve"
+ style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421"
+ version="1.1"
+ viewBox="0 0 16 16"
+ width="16"
+ xlink="http://www.w3.org/1999/xlink"
+ >
+ <path
+ d="M14.7 3.4v3.3c0 .1 0 .2-.1.2s-.2 0-.3-.1l-.9-.9-4.8 4.8c-.1.1-.1.1-.2.1s-.1 0-.2-.1L6.4 9l-3.2 3.2-1.5-1.5 4.5-4.5c.1-.1.1-.1.2-.1s.1 0 .2.1L8.4 8l3.5-3.5-.9-1c-.1-.1-.1-.2-.1-.3s.1-.1.2-.1h3.3c.1 0 .1 0 .2.1.1 0 .1.1.1.2z"
+ style="fill:currentColor"
+ />
+ </svg>
+ <span>
+ project_activity.page
+ </span>
+ </a>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div
+ class="overview-domain-leak"
+ >
+ <div
+ class="overview-domain-measures"
+ >
+ <div
+ class="overview-domain-measure"
+ >
+ <div
+ class="overview-domain-measure-value"
+ >
+ <div>
+ <a>
+ <span
+ class="js-overview-main-new-coverage"
+ >
+ 1.0%
+ </span>
+ </a>
+ </div>
+ </div>
+ <div
+ class="overview-domain-measure-label"
+ >
+ overview.coverage_on
+ <br />
+ <a
+ class="spacer-right overview-domain-secondary-measure-value"
+ >
+ <span
+ class="js-overview-main-new-coverage"
+ >
+ 1
+ </span>
+ </a>
+ overview.metric.new_lines_to_cover
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
+`;
diff --git a/server/sonar-web/src/main/js/apps/overview/main/__tests__/__snapshots__/Duplications-test.tsx.snap b/server/sonar-web/src/main/js/apps/overview/main/__tests__/__snapshots__/Duplications-test.tsx.snap
new file mode 100644
index 00000000000..7c06ddb9058
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/overview/main/__tests__/__snapshots__/Duplications-test.tsx.snap
@@ -0,0 +1,166 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render correctly 1`] = `
+<div
+ class="overview-card"
+>
+ <div
+ class="overview-card-header"
+ >
+ <div
+ class="overview-title"
+ >
+ <span>
+ overview.domain.duplications
+ </span>
+ <a
+ class="spacer-left small"
+ >
+ layout.measures
+ </a>
+ </div>
+ </div>
+ <div
+ class="overview-domain-panel"
+ >
+ <div
+ class="overview-domain-nutshell"
+ >
+ <div
+ class="overview-domain-measures overview-domain-measures-big"
+ >
+ <div
+ class="overview-domain-measure"
+ >
+ <div
+ class="display-inline-block text-middle big-spacer-right neg-offset-left"
+ >
+ <div
+ class="duplications-rating duplications-rating-big duplications-rating-A"
+ />
+ </div>
+ <div
+ class="display-inline-block text-middle"
+ >
+ <div
+ class="overview-domain-measure-value"
+ >
+ <a>
+ 0.5%
+ </a>
+ </div>
+ <div
+ class="overview-domain-measure-label display-flex-center display-flex-justify-center"
+ >
+ overview.metric.duplications
+ </div>
+ <a
+ class="overview-domain-measure-history-link"
+ >
+ <svg
+ height="16"
+ space="preserve"
+ style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421"
+ version="1.1"
+ viewBox="0 0 16 16"
+ width="16"
+ xlink="http://www.w3.org/1999/xlink"
+ >
+ <path
+ d="M14.7 3.4v3.3c0 .1 0 .2-.1.2s-.2 0-.3-.1l-.9-.9-4.8 4.8c-.1.1-.1.1-.2.1s-.1 0-.2-.1L6.4 9l-3.2 3.2-1.5-1.5 4.5-4.5c.1-.1.1-.1.2-.1s.1 0 .2.1L8.4 8l3.5-3.5-.9-1c-.1-.1-.1-.2-.1-.3s.1-.1.2-.1h3.3c.1 0 .1 0 .2.1.1 0 .1.1.1.2z"
+ style="fill:currentColor"
+ />
+ </svg>
+ <span>
+ project_activity.page
+ </span>
+ </a>
+ </div>
+ </div>
+ <div
+ class="overview-domain-measure"
+ >
+ <div
+ class="overview-domain-measure-value"
+ >
+ <a>
+ <span
+ class="js-overview-main-tests"
+ >
+ 1
+ </span>
+ </a>
+ </div>
+ <div
+ class="overview-domain-measure-label display-flex-center display-flex-justify-center"
+ >
+ Duplicated Blocks
+ <a
+ class="overview-domain-measure-history-link"
+ >
+ <svg
+ height="16"
+ space="preserve"
+ style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421"
+ version="1.1"
+ viewBox="0 0 16 16"
+ width="16"
+ xlink="http://www.w3.org/1999/xlink"
+ >
+ <path
+ d="M14.7 3.4v3.3c0 .1 0 .2-.1.2s-.2 0-.3-.1l-.9-.9-4.8 4.8c-.1.1-.1.1-.2.1s-.1 0-.2-.1L6.4 9l-3.2 3.2-1.5-1.5 4.5-4.5c.1-.1.1-.1.2-.1s.1 0 .2.1L8.4 8l3.5-3.5-.9-1c-.1-.1-.1-.2-.1-.3s.1-.1.2-.1h3.3c.1 0 .1 0 .2.1.1 0 .1.1.1.2z"
+ style="fill:currentColor"
+ />
+ </svg>
+ <span>
+ project_activity.page
+ </span>
+ </a>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div
+ class="overview-domain-leak"
+ >
+ <div
+ class="overview-domain-measures"
+ >
+ <div
+ class="overview-domain-measure"
+ >
+ <div
+ class="overview-domain-measure-value"
+ >
+ <div>
+ <a>
+ <span
+ class="js-overview-main-new-duplications"
+ >
+ 1.5%
+ </span>
+ </a>
+ </div>
+ </div>
+ <div
+ class="overview-domain-measure-label"
+ >
+ overview.duplications_on
+ <br />
+ <a
+ class="spacer-right overview-domain-secondary-measure-value"
+ >
+ <span
+ class="js-overview-main-new-lines"
+ >
+ 1
+ </span>
+ </a>
+ overview.metric.new_lines
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
+`;
diff --git a/server/sonar-web/src/main/js/apps/overview/main/__tests__/__snapshots__/VulnerabilitiesAndHotspots-test.tsx.snap b/server/sonar-web/src/main/js/apps/overview/main/__tests__/__snapshots__/VulnerabilitiesAndHotspots-test.tsx.snap
new file mode 100644
index 00000000000..826e2a6bb8c
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/overview/main/__tests__/__snapshots__/VulnerabilitiesAndHotspots-test.tsx.snap
@@ -0,0 +1,288 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render correctly 1`] = `
+<div
+ class="overview-card"
+>
+ <div
+ class="overview-card-header"
+ >
+ <div
+ class="overview-title"
+ >
+ <span>
+ metric_domain.Security
+ </span>
+ <a
+ class="spacer-left small"
+ >
+ layout.measures
+ </a>
+ </div>
+ </div>
+ <div
+ class="overview-domain-panel"
+ >
+ <div
+ class="overview-domain-nutshell"
+ >
+ <div
+ class="overview-domain-measures"
+ >
+ <div
+ class="overview-domain-measure"
+ >
+ <div
+ class="overview-domain-measure-value"
+ >
+ <span
+ class="offset-left"
+ >
+ <a>
+ 0
+ </a>
+ </span>
+ <div
+ class="overview-domain-measure-sup"
+ >
+ <a
+ class="link-no-underline"
+ >
+ <span
+ aria-label="metric.has_rating_X.A"
+ class="rating rating-A"
+ >
+ A
+ </span>
+ </a>
+ </div>
+ </div>
+ <div
+ class="overview-domain-measure-label display-flex-center display-flex-justify-center"
+ >
+ <svg
+ class="little-spacer-right"
+ height="16"
+ space="preserve"
+ style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421"
+ version="1.1"
+ viewBox="0 0 16 16"
+ width="16"
+ xlink="http://www.w3.org/1999/xlink"
+ >
+ <path
+ d="M10.8 5H6V3.9a2.28 2.28 0 0 1 2-2.5 2.22 2.22 0 0 1 1.8 1.2.48.48 0 0 0 .7.2.48.48 0 0 0 .2-.7A3 3 0 0 0 8 .4a3.34 3.34 0 0 0-3 3.5v1.2a2.16 2.16 0 0 0-2 2.1v4.4a2.22 2.22 0 0 0 2.2 2.2h5.6a2.22 2.22 0 0 0 2.2-2.2V7.2A2.22 2.22 0 0 0 10.8 5zm-2.2 5.5v1.2H7.4v-1.2a1.66 1.66 0 0 1-1.1-1.6A1.75 1.75 0 0 1 8 7.2a1.71 1.71 0 0 1 .6 3.3z"
+ style="fill:currentColor"
+ />
+ </svg>
+ overview.metric.vulnerabilities
+ </div>
+ <a
+ class="overview-domain-measure-history-link"
+ >
+ <svg
+ height="16"
+ space="preserve"
+ style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421"
+ version="1.1"
+ viewBox="0 0 16 16"
+ width="16"
+ xlink="http://www.w3.org/1999/xlink"
+ >
+ <path
+ d="M14.7 3.4v3.3c0 .1 0 .2-.1.2s-.2 0-.3-.1l-.9-.9-4.8 4.8c-.1.1-.1.1-.2.1s-.1 0-.2-.1L6.4 9l-3.2 3.2-1.5-1.5 4.5-4.5c.1-.1.1-.1.2-.1s.1 0 .2.1L8.4 8l3.5-3.5-.9-1c-.1-.1-.1-.2-.1-.3s.1-.1.2-.1h3.3c.1 0 .1 0 .2.1.1 0 .1.1.1.2z"
+ style="fill:currentColor"
+ />
+ </svg>
+ <span>
+ project_activity.page
+ </span>
+ </a>
+ </div>
+ <div
+ class="overview-domain-measure"
+ >
+ <div
+ class="overview-domain-measure-value overview-domain-measure-value-small"
+ >
+ <a>
+ 0
+ </a>
+ </div>
+ <div
+ class="overview-domain-measure-label display-flex-center display-flex-justify-center"
+ >
+ <svg
+ class="little-spacer-right"
+ height="16"
+ space="preserve"
+ style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421"
+ version="1.1"
+ viewBox="0 0 16 16"
+ width="16"
+ xlink="http://www.w3.org/1999/xlink"
+ >
+ <g
+ style="fill:currentColor"
+ >
+ <path
+ d="M10.238 2.416c-0.432-0.895-1.259-1.504-2.202-1.504-1.386 0-2.521 1.318-2.521 2.927v5.481"
+ fill="none"
+ stroke="currentColor"
+ stroke-linecap="round"
+ stroke-width="1.1429"
+ />
+ <path
+ d="M8.537 10.372v1.199h-1.099v-1.199c-0.638-0.228-1.099-0.832-1.099-1.546 0-0.909 0.739-1.649 1.648-1.649s1.649 0.74 1.649 1.649c0 0.715-0.461 1.32-1.099 1.546zM10.734 4.979h-5.494c-1.21 0-2.199 0.989-2.199 2.197v4.395c0 1.21 0.989 2.199 2.199 2.199h5.494c1.209 0 2.197-0.989 2.197-2.199v-4.395c0-1.209-0.989-2.197-2.197-2.197z"
+ />
+ <path
+ d="M4.030 6.352h6.923v6.923h-6.923z"
+ />
+ <path
+ d="M7.504 10.283c0-0.423 0.048-0.757 0.144-1.002s0.251-0.457 0.465-0.637c0.215-0.18 0.377-0.344 0.489-0.493s0.167-0.313 0.167-0.493c0-0.438-0.189-0.656-0.565-0.656-0.174 0-0.314 0.064-0.421 0.191s-0.164 0.3-0.17 0.518h-1.469c0.006-0.58 0.189-1.031 0.548-1.354s0.864-0.485 1.513-0.485c0.646 0 1.147 0.149 1.501 0.447s0.532 0.723 0.532 1.274c0 0.241-0.048 0.459-0.144 0.656s-0.249 0.398-0.46 0.604l-0.5 0.465c-0.142 0.136-0.241 0.276-0.296 0.42s-0.086 0.325-0.091 0.545h-1.243zM7.326 11.604c0-0.215 0.078-0.39 0.233-0.528s0.349-0.207 0.58-0.207c0.232 0 0.425 0.068 0.58 0.207s0.233 0.313 0.233 0.528-0.078 0.39-0.233 0.528c-0.155 0.138-0.349 0.207-0.58 0.207s-0.425-0.068-0.58-0.207c-0.155-0.138-0.233-0.313-0.233-0.528z"
+ fill="#fff"
+ />
+ </g>
+ </svg>
+ overview.metric.security_hotspots
+ </div>
+ <a
+ class="overview-domain-measure-history-link"
+ >
+ <svg
+ height="16"
+ space="preserve"
+ style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421"
+ version="1.1"
+ viewBox="0 0 16 16"
+ width="16"
+ xlink="http://www.w3.org/1999/xlink"
+ >
+ <path
+ d="M14.7 3.4v3.3c0 .1 0 .2-.1.2s-.2 0-.3-.1l-.9-.9-4.8 4.8c-.1.1-.1.1-.2.1s-.1 0-.2-.1L6.4 9l-3.2 3.2-1.5-1.5 4.5-4.5c.1-.1.1-.1.2-.1s.1 0 .2.1L8.4 8l3.5-3.5-.9-1c-.1-.1-.1-.2-.1-.3s.1-.1.2-.1h3.3c.1 0 .1 0 .2.1.1 0 .1.1.1.2z"
+ style="fill:currentColor"
+ />
+ </svg>
+ <span>
+ project_activity.page
+ </span>
+ </a>
+ </div>
+ </div>
+ <div
+ class="overview-domain-timeline"
+ />
+ </div>
+ <div
+ class="overview-domain-leak"
+ >
+ <div
+ class="overview-domain-measures"
+ >
+ <div
+ class="overview-domain-measure"
+ >
+ <div
+ class="overview-domain-measure-value"
+ >
+ <span
+ class="offset-left"
+ >
+ <a>
+ 1
+ </a>
+ </span>
+ <div
+ class="overview-domain-measure-sup"
+ >
+ <a
+ class="link-no-underline"
+ >
+ <span
+ aria-label="metric.has_rating_X.E"
+ class="rating rating-E"
+ >
+ E
+ </span>
+ </a>
+ </div>
+ </div>
+ <div
+ class="overview-domain-measure-label"
+ >
+ <svg
+ class="little-spacer-right"
+ height="16"
+ space="preserve"
+ style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421"
+ version="1.1"
+ viewBox="0 0 16 16"
+ width="16"
+ xlink="http://www.w3.org/1999/xlink"
+ >
+ <path
+ d="M10.8 5H6V3.9a2.28 2.28 0 0 1 2-2.5 2.22 2.22 0 0 1 1.8 1.2.48.48 0 0 0 .7.2.48.48 0 0 0 .2-.7A3 3 0 0 0 8 .4a3.34 3.34 0 0 0-3 3.5v1.2a2.16 2.16 0 0 0-2 2.1v4.4a2.22 2.22 0 0 0 2.2 2.2h5.6a2.22 2.22 0 0 0 2.2-2.2V7.2A2.22 2.22 0 0 0 10.8 5zm-2.2 5.5v1.2H7.4v-1.2a1.66 1.66 0 0 1-1.1-1.6A1.75 1.75 0 0 1 8 7.2a1.71 1.71 0 0 1 .6 3.3z"
+ style="fill:currentColor"
+ />
+ </svg>
+ overview.metric.new_vulnerabilities
+ </div>
+ </div>
+ <div
+ class="overview-domain-measure"
+ >
+ <div
+ class="overview-domain-measure-value overview-domain-measure-value-small"
+ >
+ <span>
+ <a>
+ 10
+ </a>
+ </span>
+ </div>
+ <div
+ class="overview-domain-measure-label"
+ >
+ <svg
+ class="little-spacer-right"
+ height="16"
+ space="preserve"
+ style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421"
+ version="1.1"
+ viewBox="0 0 16 16"
+ width="16"
+ xlink="http://www.w3.org/1999/xlink"
+ >
+ <g
+ style="fill:currentColor"
+ >
+ <path
+ d="M10.238 2.416c-0.432-0.895-1.259-1.504-2.202-1.504-1.386 0-2.521 1.318-2.521 2.927v5.481"
+ fill="none"
+ stroke="currentColor"
+ stroke-linecap="round"
+ stroke-width="1.1429"
+ />
+ <path
+ d="M8.537 10.372v1.199h-1.099v-1.199c-0.638-0.228-1.099-0.832-1.099-1.546 0-0.909 0.739-1.649 1.648-1.649s1.649 0.74 1.649 1.649c0 0.715-0.461 1.32-1.099 1.546zM10.734 4.979h-5.494c-1.21 0-2.199 0.989-2.199 2.197v4.395c0 1.21 0.989 2.199 2.199 2.199h5.494c1.209 0 2.197-0.989 2.197-2.199v-4.395c0-1.209-0.989-2.197-2.197-2.197z"
+ />
+ <path
+ d="M4.030 6.352h6.923v6.923h-6.923z"
+ />
+ <path
+ d="M7.504 10.283c0-0.423 0.048-0.757 0.144-1.002s0.251-0.457 0.465-0.637c0.215-0.18 0.377-0.344 0.489-0.493s0.167-0.313 0.167-0.493c0-0.438-0.189-0.656-0.565-0.656-0.174 0-0.314 0.064-0.421 0.191s-0.164 0.3-0.17 0.518h-1.469c0.006-0.58 0.189-1.031 0.548-1.354s0.864-0.485 1.513-0.485c0.646 0 1.147 0.149 1.501 0.447s0.532 0.723 0.532 1.274c0 0.241-0.048 0.459-0.144 0.656s-0.249 0.398-0.46 0.604l-0.5 0.465c-0.142 0.136-0.241 0.276-0.296 0.42s-0.086 0.325-0.091 0.545h-1.243zM7.326 11.604c0-0.215 0.078-0.39 0.233-0.528s0.349-0.207 0.58-0.207c0.232 0 0.425 0.068 0.58 0.207s0.233 0.313 0.233 0.528-0.078 0.39-0.233 0.528c-0.155 0.138-0.349 0.207-0.58 0.207s-0.425-0.068-0.58-0.207c-0.155-0.138-0.233-0.313-0.233-0.528z"
+ fill="#fff"
+ />
+ </g>
+ </svg>
+ overview.metric.new_security_hotspots
+ </div>
+ </div>
+ </div>
+ <div
+ class="overview-domain-timeline"
+ />
+ </div>
+ </div>
+</div>
+`;
diff --git a/server/sonar-web/src/main/js/apps/overview/main/enhance.tsx b/server/sonar-web/src/main/js/apps/overview/main/enhance.tsx
index 6ce6f657113..72cda201606 100644
--- a/server/sonar-web/src/main/js/apps/overview/main/enhance.tsx
+++ b/server/sonar-web/src/main/js/apps/overview/main/enhance.tsx
@@ -20,11 +20,11 @@
import * as React from 'react';
import { Link } from 'react-router';
import DrilldownLink from '../../../components/shared/DrilldownLink';
-import BubblesIcon from '../../../components/icons-components/BubblesIcon';
import HistoryIcon from '../../../components/icons-components/HistoryIcon';
import Rating from '../../../components/ui/Rating';
import Timeline from '../components/Timeline';
import Tooltip from '../../../components/controls/Tooltip';
+import { getWrappedDisplayName } from '../../../components/hoc/utils';
import {
formatMeasure,
isDiffMetric,
@@ -32,7 +32,7 @@ import {
getShortType,
getRatingTooltip
} from '../../../helpers/measures';
-import { getLocalizedMetricName } from '../../../helpers/l10n';
+import { getLocalizedMetricName, translate } from '../../../helpers/l10n';
import { getPeriodDate } from '../../../helpers/periods';
import {
getComponentDrilldownUrl,
@@ -54,8 +54,8 @@ export interface EnhanceProps {
export interface ComposedProps extends EnhanceProps {
getValue: (measure: T.MeasureEnhanced) => string | undefined;
- renderHeader: (domain: string, label: string) => React.ReactNode;
- renderMeasure: (metricKey: string) => React.ReactNode;
+ renderHeader: (domain: string, label?: string) => React.ReactNode;
+ renderMeasure: (metricKey: string, tooltip?: React.ReactNode) => React.ReactNode;
renderRating: (metricKey: string) => React.ReactNode;
renderIssues: (metric: string, type: T.IssueType) => React.ReactNode;
renderHistoryLink: (metricKey: string) => React.ReactNode;
@@ -64,7 +64,7 @@ export interface ComposedProps extends EnhanceProps {
export default function enhance(ComposedComponent: React.ComponentType<ComposedProps>) {
return class extends React.PureComponent<EnhanceProps> {
- static displayName = `enhance(${ComposedComponent.displayName})}`;
+ static displayName = getWrappedDisplayName(ComposedComponent, 'enhance');
getValue = (measure: T.MeasureEnhanced) => {
const { leakPeriod } = this.props;
@@ -76,27 +76,28 @@ export default function enhance(ComposedComponent: React.ComponentType<ComposedP
: measure.value;
};
- renderHeader = (domain: string, label: string) => {
+ renderHeader = (domain: string, label?: string) => {
const { branchLike, component } = this.props;
+ label = label !== undefined ? label : translate('metric_domain', domain);
return (
<div className="overview-card-header">
<div className="overview-title">
<span>{label}</span>
<Link
- className="button button-small spacer-left text-text-bottom"
+ className="spacer-left small"
to={getComponentDrilldownUrl({
componentKey: component.key,
metric: domain,
branchLike
})}>
- <BubblesIcon size={14} />
+ {translate('layout.measures')}
</Link>
</div>
</div>
);
};
- renderMeasure = (metricKey: string) => {
+ renderMeasure = (metricKey: string, tooltip?: React.ReactNode) => {
const { branchLike, measures, component } = this.props;
const measure = measures.find(measure => measure.metric.key === metricKey);
if (!measure) {
@@ -113,8 +114,9 @@ export default function enhance(ComposedComponent: React.ComponentType<ComposedP
</DrilldownLink>
</div>
- <div className="overview-domain-measure-label offset-left">
+ <div className="overview-domain-measure-label display-flex-center display-flex-justify-center">
{getLocalizedMetricName(measure.metric)}
+ {tooltip}
{this.renderHistoryLink(measure.metric.key)}
</div>
</div>
@@ -149,7 +151,7 @@ export default function enhance(ComposedComponent: React.ComponentType<ComposedP
const { branchLike, measures, component } = this.props;
const measure = measures.find(measure => measure.metric.key === metric);
if (!measure) {
- return null;
+ return <span>—</span>;
}
const value = this.getValue(measure);
@@ -166,12 +168,13 @@ export default function enhance(ComposedComponent: React.ComponentType<ComposedP
};
renderHistoryLink = (metricKey: string) => {
- const linkClass = 'button button-small spacer-left overview-domain-measure-history-link';
+ const linkClass = 'overview-domain-measure-history-link';
return (
<Link
className={linkClass}
to={getMeasureHistoryUrl(this.props.component.key, metricKey, this.props.branchLike)}>
<HistoryIcon />
+ <span>{translate('project_activity.page')}</span>
</Link>
);
};
diff --git a/server/sonar-web/src/main/js/apps/overview/styles.css b/server/sonar-web/src/main/js/apps/overview/styles.css
index 3e552f55f3c..ef63faa08ef 100644
--- a/server/sonar-web/src/main/js/apps/overview/styles.css
+++ b/server/sonar-web/src/main/js/apps/overview/styles.css
@@ -44,6 +44,7 @@
.overview-title {
font-size: var(--bigFontSize);
font-weight: 400;
+ line-height: 1.3;
}
/*
@@ -162,12 +163,16 @@
}
.overview-card {
- margin: 15px 0;
+ margin: calc(2 * var(--gridSize)) 0;
+ padding-top: 3px;
}
-.overview-card-special {
- padding-bottom: 26px;
- border-bottom: 1px solid var(--barBorderColor);
+.overview-card .offset-left {
+ margin-left: 30px;
+}
+
+.overview-card .neg-offset-left {
+ margin-left: -30px;
}
.overview-card-header {
@@ -189,7 +194,6 @@
.overview-domain-leak {
position: relative;
display: flex;
- padding: 15px 10px;
}
.overview-domain-nutshell {
@@ -218,26 +222,18 @@
z-index: var(--normalZIndex);
display: flex;
flex: 1;
- align-items: center;
padding: 0;
}
-.overview-domain-measures + .overview-domain-measures {
- margin-top: 30px;
-}
-
-.overview-domain-measures + .overview-domain-measures .overview-domain-measure-value {
- font-size: var(--mediumFontSize);
- font-weight: 400;
-}
-
-.overview-domain-measures + .overview-domain-measures .overview-domain-measure-label {
- margin-top: 4px;
-}
-
.overview-domain-measure {
flex: 1;
text-align: center;
+ padding: 15px 10px;
+ position: relative;
+}
+
+.overview-domain-measures-big .overview-domain-measure {
+ padding-top: 24px;
}
.overview-domain-measure-value {
@@ -247,6 +243,11 @@
white-space: nowrap;
}
+.overview-domain-measure-value-small {
+ font-size: var(--hugeFontSize);
+ margin-top: 12px;
+}
+
.overview-domain-secondary-measure-value {
line-height: 1;
font-size: 20px;
@@ -257,23 +258,18 @@
margin-top: 10px;
}
-.overview-domain-measure-label.offset-left {
- margin-right: -30px;
-}
-
-.overview-domain-measure-label > svg {
- margin-top: 3px;
-}
-
-.overview-domain-leak .overview-domain-measure-label > svg {
- margin-top: 0;
-}
-
.overview-domain-measure-history-link {
- vertical-align: bottom;
+ position: absolute;
+ top: var(--gridSize);
+ right: var(--gridSize);
+ border-bottom: 0;
visibility: hidden;
}
+.overview-domain-measure-history-link span {
+ border-bottom: 1px solid var(--lightBlue);
+}
+
.overview-domain-measure:hover .overview-domain-measure-history-link {
visibility: visible;
}
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 9a490f6098a..4aab1ebe562 100644
--- a/server/sonar-web/src/main/js/apps/overview/utils.ts
+++ b/server/sonar-web/src/main/js/apps/overview/utils.ts
@@ -40,6 +40,8 @@ export const METRICS = [
'new_vulnerabilities',
'security_rating',
'new_security_rating',
+ 'security_hotspots',
+ 'new_security_hotspots',
// code smells
'code_smells',
@@ -84,6 +86,8 @@ export const PR_METRICS = [
];
export const HISTORY_METRICS_LIST = [
+ 'bugs',
+ 'vulnerabilities',
'sqale_index',
'duplicated_lines_density',
'ncloc',
diff --git a/server/sonar-web/src/main/js/apps/securityReports/components/VulnerabilityList.tsx b/server/sonar-web/src/main/js/apps/securityReports/components/VulnerabilityList.tsx
index 95dc4630e29..22bf4655635 100755
--- a/server/sonar-web/src/main/js/apps/securityReports/components/VulnerabilityList.tsx
+++ b/server/sonar-web/src/main/js/apps/securityReports/components/VulnerabilityList.tsx
@@ -301,7 +301,7 @@ export default class VulnerabilityList extends React.PureComponent<Props, State>
<th className="text-right security-column-separator" colSpan={3}>
<div className="display-inline-flex-center">
<SecurityHotspotIcon className="spacer-right" />
- {translate('security_reports.list.hotspots')}
+ {translate('security_reports.list.security_hotspots')}
</div>
</th>
</tr>
diff --git a/server/sonar-web/src/main/js/apps/securityReports/components/__tests__/__snapshots__/VulnerabilityList-test.tsx.snap b/server/sonar-web/src/main/js/apps/securityReports/components/__tests__/__snapshots__/VulnerabilityList-test.tsx.snap
index 238d720bad0..98c06bbb330 100644
--- a/server/sonar-web/src/main/js/apps/securityReports/components/__tests__/__snapshots__/VulnerabilityList-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/securityReports/components/__tests__/__snapshots__/VulnerabilityList-test.tsx.snap
@@ -36,7 +36,7 @@ exports[`renders 1`] = `
<SecurityHotspotIcon
className="spacer-right"
/>
- security_reports.list.hotspots
+ security_reports.list.security_hotspots
</div>
</th>
</tr>
@@ -504,7 +504,7 @@ exports[`renders with cwe 1`] = `
<SecurityHotspotIcon
className="spacer-right"
/>
- security_reports.list.hotspots
+ security_reports.list.security_hotspots
</div>
</th>
</tr>
diff --git a/server/sonar-web/src/main/js/helpers/testMocks.ts b/server/sonar-web/src/main/js/helpers/testMocks.ts
index 3d52b4e8098..dd3b736a845 100644
--- a/server/sonar-web/src/main/js/helpers/testMocks.ts
+++ b/server/sonar-web/src/main/js/helpers/testMocks.ts
@@ -268,6 +268,23 @@ export function mockMeasure(overrides: Partial<T.Measure> = {}): T.Measure {
};
}
+export function mockMeasureEnhanced(overrides: Partial<T.MeasureEnhanced> = {}): T.MeasureEnhanced {
+ return {
+ bestValue: true,
+ leak: '1',
+ metric: mockMetric({ ...(overrides.metric || {}) }),
+ periods: [
+ {
+ bestValue: true,
+ index: 1,
+ value: '1.0'
+ }
+ ],
+ value: '1.0',
+ ...overrides
+ };
+}
+
export function mockOrganization(overrides: Partial<T.Organization> = {}): T.Organization {
return { key: 'foo', name: 'Foo', ...overrides };
}
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 a2196a1509c..61016a97cee 100644
--- a/sonar-core/src/main/resources/org/sonar/l10n/core.properties
+++ b/sonar-core/src/main/resources/org/sonar/l10n/core.properties
@@ -2089,7 +2089,7 @@ security_reports.owaspTop10.description=Track Vulnerabilities and Security Hotsp
security_reports.sansTop25.description=Track Vulnerabilities and Security Hotspots conforming to SANS Top 25 standard (25 CWE items in three categories).
security_reports.list.categories=Categories
security_reports.list.vulnerabilities=Vulnerabilities
-security_reports.list.hotspots=Security Hotspots
+security_reports.list.security_hotspots=Security Hotspots
security_reports.line.open=Open
security_reports.line.wont_fix=Won't Fix
security_reports.line.in_review=In Review
@@ -2454,6 +2454,8 @@ overview.metric.bugs=Bugs
overview.metric.new_bugs=New Bugs
overview.metric.vulnerabilities=Vulnerabilities
overview.metric.new_vulnerabilities=New Vulnerabilities
+overview.metric.security_hotspots=Security Hotspots
+overview.metric.new_security_hotspots=New Security Hotspots
overview.metric.issues=Issues
overview.metric.effort=Debt
overview.metric.new_issues=New Issues