aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStas Vilchik <vilchiks@gmail.com>2015-11-12 18:05:08 +0100
committerStas Vilchik <vilchiks@gmail.com>2015-11-13 14:25:00 +0100
commit27c49eda28940ca7600587b75fcbc9223268ee8b (patch)
treeb296526a4426370c20cff34aa55cc0097ee87d01
parent3348f196c986cfc9db1cfeb2a57ae847c2f4df63 (diff)
downloadsonarqube-27c49eda28940ca7600587b75fcbc9223268ee8b.tar.gz
sonarqube-27c49eda28940ca7600587b75fcbc9223268ee8b.zip
SONAR-6331 refactor overview code
-rw-r--r--server/sonar-web/package.json1
-rw-r--r--server/sonar-web/src/main/js/apps/overview/components/complexity-distribution.js (renamed from server/sonar-web/src/main/js/apps/overview/size/complexity-distribution.js)10
-rw-r--r--server/sonar-web/src/main/js/apps/overview/components/coverage-measure.js53
-rw-r--r--server/sonar-web/src/main/js/apps/overview/components/coverage-measures-list.js123
-rw-r--r--server/sonar-web/src/main/js/apps/overview/components/detailed-measure.js (renamed from server/sonar-web/src/main/js/apps/overview/common-components.js)20
-rw-r--r--server/sonar-web/src/main/js/apps/overview/components/domain-bubble-chart.js (renamed from server/sonar-web/src/main/js/apps/overview/domain/bubble-chart.js)5
-rw-r--r--server/sonar-web/src/main/js/apps/overview/components/domain-timeline.js (renamed from server/sonar-web/src/main/js/apps/overview/timeline/domain-timeline.js)5
-rw-r--r--server/sonar-web/src/main/js/apps/overview/components/domain-treemap.js (renamed from server/sonar-web/src/main/js/apps/overview/domain/treemap.js)5
-rw-r--r--server/sonar-web/src/main/js/apps/overview/components/issue-measure.js (renamed from server/sonar-web/src/main/js/apps/overview/issues/issue-measure.js)75
-rw-r--r--server/sonar-web/src/main/js/apps/overview/components/issues-assignees.js (renamed from server/sonar-web/src/main/js/apps/overview/issues/assignees.js)9
-rw-r--r--server/sonar-web/src/main/js/apps/overview/components/issues-tags.js (renamed from server/sonar-web/src/main/js/apps/overview/issues/tags.js)12
-rw-r--r--server/sonar-web/src/main/js/apps/overview/components/language-distribution.js (renamed from server/sonar-web/src/main/js/apps/overview/size/language-distribution.js)17
-rw-r--r--server/sonar-web/src/main/js/apps/overview/components/legend.js16
-rw-r--r--server/sonar-web/src/main/js/apps/overview/components/timeline-chart.js (renamed from server/sonar-web/src/main/js/apps/overview/timeline/timeline-chart.js)0
-rw-r--r--server/sonar-web/src/main/js/apps/overview/coverage/coverage-measure.js85
-rw-r--r--server/sonar-web/src/main/js/apps/overview/coverage/main.js184
-rw-r--r--server/sonar-web/src/main/js/apps/overview/domain/header.js7
-rw-r--r--server/sonar-web/src/main/js/apps/overview/domain/measures-list.js57
-rw-r--r--server/sonar-web/src/main/js/apps/overview/domain/timeline.js7
-rw-r--r--server/sonar-web/src/main/js/apps/overview/domains/coverage-domain.js119
-rw-r--r--server/sonar-web/src/main/js/apps/overview/domains/debt-domain.js (renamed from server/sonar-web/src/main/js/apps/overview/issues/main.js)74
-rw-r--r--server/sonar-web/src/main/js/apps/overview/domains/duplications-domain.js (renamed from server/sonar-web/src/main/js/apps/overview/duplications/main.js)50
-rw-r--r--server/sonar-web/src/main/js/apps/overview/domains/size-domain.js (renamed from server/sonar-web/src/main/js/apps/overview/size/main.js)35
-rw-r--r--server/sonar-web/src/main/js/apps/overview/gate/gate-condition.js (renamed from server/sonar-web/src/main/js/apps/overview/main/gate/gate-condition.js)17
-rw-r--r--server/sonar-web/src/main/js/apps/overview/gate/gate-conditions.js (renamed from server/sonar-web/src/main/js/apps/overview/main/gate/gate-conditions.js)0
-rw-r--r--server/sonar-web/src/main/js/apps/overview/gate/gate-empty.js (renamed from server/sonar-web/src/main/js/apps/overview/main/gate/gate-empty.js)0
-rw-r--r--server/sonar-web/src/main/js/apps/overview/gate/gate.js (renamed from server/sonar-web/src/main/js/apps/overview/main/gate/gate.js)0
-rw-r--r--server/sonar-web/src/main/js/apps/overview/helpers/donut.js34
-rw-r--r--server/sonar-web/src/main/js/apps/overview/helpers/measure-variation.js13
-rw-r--r--server/sonar-web/src/main/js/apps/overview/helpers/measure.js12
-rw-r--r--server/sonar-web/src/main/js/apps/overview/helpers/periods.js (renamed from server/sonar-web/src/main/js/apps/overview/helpers/period-label.js)10
-rw-r--r--server/sonar-web/src/main/js/apps/overview/issues/severities.js36
-rw-r--r--server/sonar-web/src/main/js/apps/overview/main/components.js6
-rw-r--r--server/sonar-web/src/main/js/apps/overview/main/coverage.js2
-rw-r--r--server/sonar-web/src/main/js/apps/overview/main/duplications.js2
-rw-r--r--server/sonar-web/src/main/js/apps/overview/main/issues.js11
-rw-r--r--server/sonar-web/src/main/js/apps/overview/main/main.js4
-rw-r--r--server/sonar-web/src/main/js/apps/overview/main/size.js4
-rw-r--r--server/sonar-web/src/main/js/apps/overview/meta.js8
-rw-r--r--server/sonar-web/src/main/js/apps/overview/overview.js10
-rw-r--r--server/sonar-web/src/main/js/components/shared/drilldown-link.js (renamed from server/sonar-web/src/main/js/apps/overview/helpers/drilldown-link.js)45
-rw-r--r--server/sonar-web/src/main/js/components/shared/issues-link.js (renamed from server/sonar-web/src/main/js/apps/overview/helpers/issues-link.js)5
-rw-r--r--server/sonar-web/src/main/js/components/shared/quality-gate-link.js (renamed from server/sonar-web/src/main/js/apps/overview/helpers/gate-link.js)3
-rw-r--r--server/sonar-web/src/main/js/components/shared/quality-profile-link.js (renamed from server/sonar-web/src/main/js/apps/overview/helpers/profile-link.js)3
-rw-r--r--server/sonar-web/src/main/js/components/shared/rating.js (renamed from server/sonar-web/src/main/js/apps/overview/helpers/rating.js)6
-rw-r--r--server/sonar-web/src/main/less/init/misc.less4
-rw-r--r--server/sonar-web/src/main/less/pages/overview.less120
-rw-r--r--server/sonar-web/tests/apps/overview/components/complexity-distribution-test.js41
-rw-r--r--server/sonar-web/tests/apps/overview/components/issues-tags-test.js45
-rw-r--r--server/sonar-web/tests/apps/overview/components/language-distribution-test.js38
-rw-r--r--server/sonar-web/tests/apps/overview/components/legend-test.js26
-rw-r--r--server/sonar-web/tests/apps/overview/helpers/metrics-test.js110
-rw-r--r--server/sonar-web/tests/apps/overview/helpers/periods-test.js57
-rw-r--r--server/sonar-web/tests/apps/overview/overview-test.js (renamed from server/sonar-web/tests/apps/overview-test.js)13
54 files changed, 979 insertions, 675 deletions
diff --git a/server/sonar-web/package.json b/server/sonar-web/package.json
index 87a6ac744b1..10b162c4ce7 100644
--- a/server/sonar-web/package.json
+++ b/server/sonar-web/package.json
@@ -13,6 +13,7 @@
"browserify": "11.2.0",
"browserify-shim": "3.8.10",
"chai": "3.3.0",
+ "chai-datetime": "^1.4.0",
"classnames": "^2.2.0",
"d3": "3.5.6",
"del": "2.0.2",
diff --git a/server/sonar-web/src/main/js/apps/overview/size/complexity-distribution.js b/server/sonar-web/src/main/js/apps/overview/components/complexity-distribution.js
index 2c82df4d6a3..390273da2e1 100644
--- a/server/sonar-web/src/main/js/apps/overview/size/complexity-distribution.js
+++ b/server/sonar-web/src/main/js/apps/overview/components/complexity-distribution.js
@@ -7,7 +7,11 @@ import { formatMeasure } from '../../../helpers/measures';
const HEIGHT = 80;
-export class ComplexityDistribution extends React.Component {
+export const ComplexityDistribution = React.createClass({
+ propTypes: {
+ distribution: React.PropTypes.string.isRequired
+ },
+
renderBarChart () {
let data = this.props.distribution.split(';').map((point, index) => {
let tokens = point.split('=');
@@ -24,11 +28,11 @@ export class ComplexityDistribution extends React.Component {
height={HEIGHT}
barsWidth={10}
padding={[25, 0, 25, 0]}/>;
- }
+ },
render () {
return <div className="overview-bar-chart">
{this.renderBarChart()}
</div>;
}
-}
+});
diff --git a/server/sonar-web/src/main/js/apps/overview/components/coverage-measure.js b/server/sonar-web/src/main/js/apps/overview/components/coverage-measure.js
new file mode 100644
index 00000000000..df6206f03de
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/overview/components/coverage-measure.js
@@ -0,0 +1,53 @@
+import classNames from 'classnames';
+import React from 'react';
+
+import { formatMeasure, formatMeasureVariation, localizeMetric } from '../../../helpers/measures';
+import { DrilldownLink } from '../../../components/shared/drilldown-link';
+import { DonutChart } from '../../../components/charts/donut-chart';
+
+
+export const CoverageMeasure = React.createClass({
+ renderLeak () {
+ if (this.props.leak == null) {
+ return null;
+ }
+ return <div className="overview-detailed-measure-leak">
+ <span className="overview-detailed-measure-value">
+ {formatMeasureVariation(this.props.leak, 'PERCENT')}
+ </span>
+ </div>;
+ },
+
+ renderDonut () {
+ let donutData = [
+ { value: this.props.measure, fill: '#85bb43' },
+ { value: 100 - this.props.measure, fill: '#d4333f' }
+ ];
+ return <div className="overview-donut-chart">
+ <DonutChart width="90" height="90" thickness="3" data={donutData}/>
+ <div className="overview-detailed-measure-value">
+ <DrilldownLink component={this.props.component.key} metric={this.props.metric} period={this.props.period}>
+ {formatMeasure(this.props.measure, 'PERCENT')}
+ </DrilldownLink>
+ </div>
+ </div>;
+ },
+
+ render () {
+ if (this.props.measure == null) {
+ return null;
+ }
+
+ let className = classNames('overview-detailed-measure', {
+ 'overview-leak': this.props.period
+ });
+
+ return <li className={className}>
+ <div className="overview-detailed-measure-nutshell space-between">
+ <span className="overview-detailed-measure-name">{localizeMetric(this.props.metric)}</span>
+ {this.renderDonut(this.props.measure)}
+ </div>
+ {this.renderLeak()}
+ </li>;
+ }
+});
diff --git a/server/sonar-web/src/main/js/apps/overview/components/coverage-measures-list.js b/server/sonar-web/src/main/js/apps/overview/components/coverage-measures-list.js
new file mode 100644
index 00000000000..898da4b1cd0
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/overview/components/coverage-measures-list.js
@@ -0,0 +1,123 @@
+import _ from 'underscore';
+import React from 'react';
+
+import { DetailedMeasure } from '../components/detailed-measure';
+import { filterMetricsForDomains } from '../helpers/metrics';
+import { CoverageMeasure } from '../components/coverage-measure';
+
+
+const TEST_DOMAINS = ['Tests', 'Tests (Integration)', 'Tests (Overall)'];
+
+const UT_COVERAGE_METRICS = ['coverage', 'line_coverage', 'branch_coverage'];
+const UT_NEW_COVERAGE_METRICS = ['new_coverage', 'new_line_coverage', 'new_branch_coverage'];
+
+const IT_COVERAGE_METRICS = ['it_coverage', 'it_line_coverage', 'it_branch_coverage'];
+const IT_NEW_COVERAGE_METRICS = ['new_it_coverage', 'new_it_line_coverage', 'new_it_branch_coverage'];
+
+const OVERALL_COVERAGE_METRICS = ['overall_coverage', 'overall_line_coverage', 'overall_branch_coverage'];
+const OVERALL_NEW_COVERAGE_METRICS = ['new_overall_coverage', 'new_overall_line_coverage',
+ 'new_overall_branch_coverage'];
+
+const TEST_METRICS = ['tests', 'test_execution_time', 'test_errors', 'test_failures', 'skipped_tests',
+ 'test_success_density'];
+
+const KNOWN_METRICS = [].concat(TEST_METRICS, OVERALL_COVERAGE_METRICS, UT_COVERAGE_METRICS, IT_COVERAGE_METRICS);
+
+
+export const CoverageMeasuresList = React.createClass({
+ renderOtherMeasures() {
+ let metrics = filterMetricsForDomains(this.props.metrics, TEST_DOMAINS)
+ .filter(metric => KNOWN_METRICS.indexOf(metric.key) === -1)
+ .map(metric => metric.key);
+ return this.renderListOfMeasures(metrics);
+ },
+
+ renderCoverage (metrics) {
+ let measures = metrics.map(metric => {
+ return <CoverageMeasure key={metric}
+ metric={metric}
+ measure={this.props.measures[metric]}
+ leak={this.props.leak[metric]}
+ component={this.props.component}/>;
+ });
+ return <div className="overview-detailed-measures-list overview-detailed-measures-list-inline">
+ {measures}
+ </div>;
+ },
+
+ shouldRenderTypedCoverage () {
+ return this.props.measures['coverage'] != null && this.props.measures['it_coverage'] != null;
+ },
+
+ renderTypedCoverage (metrics) {
+ return this.shouldRenderTypedCoverage() ? this.renderCoverage(metrics) : null;
+ },
+
+ renderNewCoverage (metrics) {
+ let measures = metrics.map(metric => {
+ return <CoverageMeasure key={metric}
+ metric={metric}
+ measure={this.props.leak[metric]}
+ component={this.props.component}
+ period={this.props.leakPeriodIndex}/>;
+ });
+ return <div className="overview-detailed-measures-list overview-detailed-measures-list-inline">
+ {measures}
+ </div>;
+ },
+
+ renderTypedNewCoverage (metrics) {
+ return this.shouldRenderTypedCoverage() ? this.renderNewCoverage(metrics) : null;
+ },
+
+ renderUTCoverage () {
+ return this.renderTypedCoverage(UT_COVERAGE_METRICS);
+ },
+
+ renderUTNewCoverage () {
+ return this.renderTypedNewCoverage(UT_NEW_COVERAGE_METRICS);
+ },
+
+ renderITCoverage () {
+ return this.renderTypedCoverage(IT_COVERAGE_METRICS);
+ },
+
+ renderITNewCoverage () {
+ return this.renderTypedNewCoverage(IT_NEW_COVERAGE_METRICS);
+ },
+
+ renderOverallCoverage () {
+ return this.renderCoverage(OVERALL_COVERAGE_METRICS);
+ },
+
+ renderOverallNewCoverage () {
+ return this.renderNewCoverage(OVERALL_NEW_COVERAGE_METRICS);
+ },
+
+ renderListOfMeasures(list) {
+ let metrics = list
+ .map(key => _.findWhere(this.props.metrics, { key }))
+ .map(metric => {
+ return <DetailedMeasure key={metric.key} {...this.props} {...this.props} metric={metric.key}
+ type={metric.type}/>;
+ });
+ return <div className="overview-detailed-measures-list">{metrics}</div>;
+ },
+
+ render () {
+ return <div>
+ {this.renderOverallCoverage()}
+ {this.renderOverallNewCoverage()}
+
+ {this.renderUTCoverage()}
+ {this.renderUTNewCoverage()}
+
+ {this.renderITCoverage()}
+ {this.renderITNewCoverage()}
+
+ {this.renderListOfMeasures(TEST_METRICS)}
+
+ {this.renderOtherMeasures()}
+ </div>;
+ }
+});
diff --git a/server/sonar-web/src/main/js/apps/overview/common-components.js b/server/sonar-web/src/main/js/apps/overview/components/detailed-measure.js
index d1299d539e8..df0b8e2630c 100644
--- a/server/sonar-web/src/main/js/apps/overview/common-components.js
+++ b/server/sonar-web/src/main/js/apps/overview/components/detailed-measure.js
@@ -1,9 +1,8 @@
import React from 'react';
-import { formatMeasure, formatMeasureVariation, localizeMetric } from '../../helpers/measures';
-import DrilldownLink from './helpers/drilldown-link';
-import { getShortType } from './helpers/metrics';
-import { DomainLeakTitle } from './main/components';
+import { formatMeasure, formatMeasureVariation, localizeMetric } from '../../../helpers/measures';
+import { DrilldownLink } from '../../../components/shared/drilldown-link';
+import { getShortType } from '../helpers/metrics';
export const DetailedMeasure = React.createClass({
@@ -39,16 +38,3 @@ export const DetailedMeasure = React.createClass({
</div>;
}
});
-
-
-export const Legend = React.createClass({
- render() {
- if (!this.props.leakPeriodDate) {
- return null;
- }
- return <div className="overview-legend">
- <span className="overview-legend-leak"/>
- <DomainLeakTitle label={this.props.leakPeriodLabel} date={this.props.leakPeriodDate}/>
- </div>;
- }
-});
diff --git a/server/sonar-web/src/main/js/apps/overview/domain/bubble-chart.js b/server/sonar-web/src/main/js/apps/overview/components/domain-bubble-chart.js
index e112d8a230d..056863ecdb4 100644
--- a/server/sonar-web/src/main/js/apps/overview/domain/bubble-chart.js
+++ b/server/sonar-web/src/main/js/apps/overview/components/domain-bubble-chart.js
@@ -1,5 +1,6 @@
import _ from 'underscore';
import React from 'react';
+
import { BubbleChart } from '../../../components/charts/bubble-chart';
import { getComponentUrl } from '../../../helpers/urls';
import { getFiles } from '../../../api/components';
@@ -101,8 +102,8 @@ export class DomainBubbleChart extends React.Component {
}
render () {
- return <div className="overview-domain overview-domain-chart">
- <div className="overview-domain-header">
+ return <div className="overview-domain-chart">
+ <div className="overview-card-header">
<h2 className="overview-title">Project Files</h2>
<ul className="list-inline small">
<li>X: {this.state.xMetric.name}</li>
diff --git a/server/sonar-web/src/main/js/apps/overview/timeline/domain-timeline.js b/server/sonar-web/src/main/js/apps/overview/components/domain-timeline.js
index 59e5280bd48..62d4ab1f9f9 100644
--- a/server/sonar-web/src/main/js/apps/overview/timeline/domain-timeline.js
+++ b/server/sonar-web/src/main/js/apps/overview/components/domain-timeline.js
@@ -11,6 +11,7 @@ import { Timeline } from './timeline-chart';
const HEIGHT = 280;
+
function parseValue (value, type) {
return type === 'RATING' && typeof value === 'string' ? value.charCodeAt(0) - 'A'.charCodeAt(0) + 1 : value;
}
@@ -185,8 +186,8 @@ export const DomainTimeline = React.createClass({
},
render () {
- return <div className="overview-domain overview-domain-chart">
- <div className="overview-domain-header">
+ return <div className="overview-domain-chart">
+ <div className="overview-card-header">
<div>
<h2 className="overview-title">Timeline</h2>
{this.renderTimelineMetricSelect()}
diff --git a/server/sonar-web/src/main/js/apps/overview/domain/treemap.js b/server/sonar-web/src/main/js/apps/overview/components/domain-treemap.js
index e22345f78d5..59ee367e312 100644
--- a/server/sonar-web/src/main/js/apps/overview/domain/treemap.js
+++ b/server/sonar-web/src/main/js/apps/overview/components/domain-treemap.js
@@ -5,6 +5,7 @@ import { Treemap } from '../../../components/charts/treemap';
import { getChildren } from '../../../api/components';
import { formatMeasure } from '../../../helpers/measures';
+
const HEIGHT = 302;
@@ -79,8 +80,8 @@ export class DomainTreemap extends React.Component {
render () {
let color = this.props.colorMetric ? <li>Color: {this.state.colorMetric.name}</li> : null;
- return <div className="overview-domain overview-domain-chart">
- <div className="overview-domain-header">
+ return <div className="overview-domain-chart">
+ <div className="overview-card-header">
<h2 className="overview-title">Treemap</h2>
<ul className="list-inline small">
<li>Size: {this.state.sizeMetric.name}</li>
diff --git a/server/sonar-web/src/main/js/apps/overview/issues/issue-measure.js b/server/sonar-web/src/main/js/apps/overview/components/issue-measure.js
index 7234696c229..8597f15eb72 100644
--- a/server/sonar-web/src/main/js/apps/overview/issues/issue-measure.js
+++ b/server/sonar-web/src/main/js/apps/overview/components/issue-measure.js
@@ -1,8 +1,9 @@
+import moment from 'moment';
import React from 'react';
-import { formatMeasure, formatMeasureVariation, localizeMetric } from '../../../helpers/measures';
-import DrilldownLink from '../helpers/drilldown-link';
-import IssuesLink from '../helpers/issues-link';
+import { formatMeasure, localizeMetric } from '../../../helpers/measures';
+import { DrilldownLink } from '../../../components/shared/drilldown-link';
+import { IssuesLink } from '../../../components/shared/issues-link';
import { getShortType } from '../helpers/metrics';
import SeverityHelper from '../../../components/shared/severity-helper';
@@ -109,6 +110,59 @@ export const AddedRemovedMeasure = React.createClass({
});
+export const AddedRemovedDebt = React.createClass({
+ renderLeak () {
+ if (!this.props.leakPeriodDate) {
+ return null;
+ }
+
+ let leak = this.props.leak[this.props.metric];
+ let added = this.props.leak[this.props.leakMetric];
+ let removed = added - leak;
+
+ return <div className="overview-detailed-measure-leak">
+ <ul>
+ <li style={{ display: 'flex', alignItems: 'baseline' }}>
+ <small className="flex-1 text-left">Added</small>
+ <DrilldownLink className="text-danger" component={this.props.component.key} metric={this.props.leakMetric}
+ period={this.props.leakPeriodIndex}>
+ <span className="overview-detailed-measure-value">
+ {formatMeasure(added, getShortType(this.props.type))}
+ </span>
+ </DrilldownLink>
+ </li>
+ <li className="little-spacer-top" style={{ display: 'flex', alignItems: 'baseline' }}>
+ <small className="flex-1 text-left">Removed</small>
+ <span className="text-success">
+ {formatMeasure(removed, getShortType(this.props.type))}
+ </span>
+ </li>
+ </ul>
+ </div>;
+ },
+
+ render () {
+ let measure = this.props.measures[this.props.metric];
+ if (measure == null) {
+ return null;
+ }
+
+ return <div className="overview-detailed-measure">
+ <div className="overview-detailed-measure-nutshell">
+ <span className="overview-detailed-measure-name">{localizeMetric(this.props.metric)}</span>
+ <span className="overview-detailed-measure-value">
+ <DrilldownLink component={this.props.component.key} metric={this.props.metric}>
+ {formatMeasure(measure, this.props.type)}
+ </DrilldownLink>
+ </span>
+ {this.props.children}
+ </div>
+ {this.renderLeak()}
+ </div>;
+ }
+});
+
+
export const OnNewCodeMeasure = React.createClass({
renderLeak () {
if (!this.props.leakPeriodDate) {
@@ -121,11 +175,12 @@ export const OnNewCodeMeasure = React.createClass({
<ul>
<li className="little-spacer-top" style={{ display: 'flex', alignItems: 'center' }}>
<small className="flex-1 text-left">On New Code</small>
- <IssuesLink component={this.props.component.key} params={{ resolved: 'false' }}>
+ <DrilldownLink component={this.props.component.key} metric={this.props.leakMetric}
+ period={this.props.leakPeriodIndex}>
<span className="overview-detailed-measure-value">
{formatMeasure(onNewCode, getShortType(this.props.type))}
</span>
- </IssuesLink>
+ </DrilldownLink>
</li>
</ul>
</div>;
@@ -172,12 +227,15 @@ export const SeverityMeasure = React.createClass({
let added = this.props.leak[this.getNewMetric()];
let removed = added - leak;
+ let createdAfter = moment(this.props.leakPeriodDate).format('YYYY-MM-DDTHH:mm:ssZZ');
+
return <div className="overview-detailed-measure-leak">
<ul>
<li style={{ display: 'flex', alignItems: 'baseline' }}>
<small className="flex-1 text-left">Added</small>
<IssuesLink className="text-danger"
- component={this.props.component.key} params={{ resolved: 'false' }}>
+ component={this.props.component.key}
+ params={{ resolved: 'false', severities: this.props.severity, createdAfter: createdAfter }}>
<span className="overview-detailed-measure-value">
{formatMeasure(added, 'SHORT_INT')}
</span>
@@ -205,9 +263,10 @@ export const SeverityMeasure = React.createClass({
<SeverityHelper severity={this.props.severity}/>
</span>
<span className="overview-detailed-measure-value">
- <DrilldownLink component={this.props.component.key} metric={this.getMetric()}>
+ <IssuesLink component={this.props.component.key}
+ params={{ resolved: 'false', severities: this.props.severity }}>
{formatMeasure(measure, 'SHORT_INT')}
- </DrilldownLink>
+ </IssuesLink>
</span>
{this.props.children}
</div>
diff --git a/server/sonar-web/src/main/js/apps/overview/issues/assignees.js b/server/sonar-web/src/main/js/apps/overview/components/issues-assignees.js
index 1d143a8ad52..126830c9eb8 100644
--- a/server/sonar-web/src/main/js/apps/overview/issues/assignees.js
+++ b/server/sonar-web/src/main/js/apps/overview/components/issues-assignees.js
@@ -1,6 +1,5 @@
import React from 'react';
import Assignee from '../../../components/shared/assignee-helper';
-import { DomainHeader } from '../domain/header';
import { getComponentIssuesUrl } from '../../../helpers/urls';
import { formatMeasure } from '../../../helpers/measures';
@@ -8,7 +7,13 @@ import { formatMeasure } from '../../../helpers/measures';
export default class extends React.Component {
render () {
let rows = this.props.assignees.map(s => {
- let href = getComponentIssuesUrl(this.props.component.key, { statuses: 'OPEN,REOPENED', assignees: s.val });
+ let params = { statuses: 'OPEN,REOPENED' };
+ if (s.val) {
+ params.assignees = s.val;
+ } else {
+ params.assigned = 'false';
+ }
+ let href = getComponentIssuesUrl(this.props.component.key, params);
return <tr key={s.val}>
<td>
<Assignee user={s.user}/>
diff --git a/server/sonar-web/src/main/js/apps/overview/issues/tags.js b/server/sonar-web/src/main/js/apps/overview/components/issues-tags.js
index 8d1644d11c4..733ac50f2c6 100644
--- a/server/sonar-web/src/main/js/apps/overview/issues/tags.js
+++ b/server/sonar-web/src/main/js/apps/overview/components/issues-tags.js
@@ -1,12 +1,12 @@
import React from 'react';
-import { DomainHeader } from '../domain/header';
+
import { WordCloud } from '../../../components/charts/word-cloud';
import { getComponentIssuesUrl } from '../../../helpers/urls';
import { formatMeasure } from '../../../helpers/measures';
-export default class extends React.Component {
- renderWordCloud () {
+export const IssuesTags = React.createClass({
+ render () {
let tags = this.props.tags.map(tag => {
let link = getComponentIssuesUrl(this.props.component.key, { resolved: 'false', tags: tag.val });
let tooltip = `Issues: ${formatMeasure(tag.count, 'SHORT_INT')}`;
@@ -14,8 +14,4 @@ export default class extends React.Component {
});
return <WordCloud items={tags}/>;
}
-
- render () {
- return this.renderWordCloud();
- }
-}
+});
diff --git a/server/sonar-web/src/main/js/apps/overview/size/language-distribution.js b/server/sonar-web/src/main/js/apps/overview/components/language-distribution.js
index d3bde4d64ae..732db2ee1f8 100644
--- a/server/sonar-web/src/main/js/apps/overview/size/language-distribution.js
+++ b/server/sonar-web/src/main/js/apps/overview/components/language-distribution.js
@@ -6,14 +6,19 @@ import { formatMeasure } from '../../../helpers/measures';
import { getLanguages } from '../../../api/languages';
-export class LanguageDistribution extends React.Component {
+export const LanguageDistribution = React.createClass({
+ propTypes: {
+ distribution: React.PropTypes.string.isRequired,
+ lines: React.PropTypes.number.isRequired
+ },
+
componentDidMount () {
this.requestLanguages();
- }
+ },
requestLanguages () {
getLanguages().then(languages => this.setState({ languages }));
- }
+ },
getLanguageName (langKey) {
if (this.state && this.state.languages) {
@@ -22,7 +27,7 @@ export class LanguageDistribution extends React.Component {
} else {
return langKey;
}
- }
+ },
renderBarChart () {
let data = this.props.distribution.split(';').map((point, index) => {
@@ -42,11 +47,11 @@ export class LanguageDistribution extends React.Component {
height={data.length * 25}
barsWidth={10}
padding={[0, 50, 0, 80]}/>;
- }
+ },
render () {
return <div className="overview-bar-chart">
{this.renderBarChart()}
</div>;
}
-}
+});
diff --git a/server/sonar-web/src/main/js/apps/overview/components/legend.js b/server/sonar-web/src/main/js/apps/overview/components/legend.js
new file mode 100644
index 00000000000..1c38b7d17b1
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/overview/components/legend.js
@@ -0,0 +1,16 @@
+import React from 'react';
+
+import { DomainLeakTitle } from '../main/components';
+
+
+export const Legend = React.createClass({
+ render() {
+ if (!this.props.leakPeriodDate) {
+ return null;
+ }
+ return <div className="overview-legend">
+ <span className="overview-legend-leak"/>
+ <DomainLeakTitle label={this.props.leakPeriodLabel} date={this.props.leakPeriodDate}/>
+ </div>;
+ }
+});
diff --git a/server/sonar-web/src/main/js/apps/overview/timeline/timeline-chart.js b/server/sonar-web/src/main/js/apps/overview/components/timeline-chart.js
index fc61b1912ff..fc61b1912ff 100644
--- a/server/sonar-web/src/main/js/apps/overview/timeline/timeline-chart.js
+++ b/server/sonar-web/src/main/js/apps/overview/components/timeline-chart.js
diff --git a/server/sonar-web/src/main/js/apps/overview/coverage/coverage-measure.js b/server/sonar-web/src/main/js/apps/overview/coverage/coverage-measure.js
deleted file mode 100644
index 00821bd35de..00000000000
--- a/server/sonar-web/src/main/js/apps/overview/coverage/coverage-measure.js
+++ /dev/null
@@ -1,85 +0,0 @@
-import React from 'react';
-
-import { formatMeasure, formatMeasureVariation, localizeMetric } from '../../../helpers/measures';
-import DrilldownLink from '../helpers/drilldown-link';
-import { getShortType } from '../helpers/metrics';
-import { DonutChart } from '../../../components/charts/donut-chart';
-
-
-export const CoverageMeasure = React.createClass({
- renderLeakVariation () {
- if (!this.props.leakPeriodDate) {
- return null;
- }
- let leak = this.props.leak[this.props.metric];
- return <div className="overview-detailed-measure-leak">
- <span className="overview-detailed-measure-value">
- {formatMeasureVariation(leak, getShortType(this.props.type))}
- </span>
- </div>;
- },
-
- renderLeakValue () {
- if (!this.props.leakPeriodDate) {
- return null;
- }
-
- if (!this.props.leakMetric) {
- return <div className="overview-detailed-measure-leak">&nbsp;</div>;
- }
-
- let leak = this.props.leak[this.props.leakMetric];
-
- let donutData = [
- { value: leak, fill: '#85bb43' },
- { value: 100 - leak, fill: '#d4333f' }
- ];
-
- return <div className="overview-detailed-measure-leak">
- <div className="overview-donut-chart">
- <DonutChart width="20" height="20" thickness="3" data={donutData}/>
- </div>
- <span className="overview-detailed-measure-value">
- <DrilldownLink component={this.props.component.key} metric={this.props.leakMetric}
- period={this.props.leakPeriodIndex}>
- {formatMeasure(leak, this.props.type)}
- </DrilldownLink>
- </span>
- </div>;
- },
-
- renderDonut (measure) {
- if (this.props.type !== 'PERCENT') {
- return null;
- }
-
- let donutData = [
- { value: measure, fill: '#85bb43' },
- { value: 100 - measure, fill: '#d4333f' }
- ];
- return <div className="overview-donut-chart">
- <DonutChart width="20" height="20" thickness="3" data={donutData}/>
- </div>;
- },
-
- render () {
- let measure = this.props.measures[this.props.metric];
- if (measure == null) {
- return null;
- }
-
- return <div className="overview-detailed-measure">
- <div className="overview-detailed-measure-nutshell">
- <span className="overview-detailed-measure-name">{localizeMetric(this.props.metric)}</span>
- {this.renderDonut(measure)}
- <span className="overview-detailed-measure-value">
- <DrilldownLink component={this.props.component.key} metric={this.props.metric}>
- {formatMeasure(measure, this.props.type)}
- </DrilldownLink>
- </span>
- </div>
- {this.renderLeakValue()}
- {this.renderLeakVariation()}
- </div>;
- }
-});
diff --git a/server/sonar-web/src/main/js/apps/overview/coverage/main.js b/server/sonar-web/src/main/js/apps/overview/coverage/main.js
deleted file mode 100644
index 1c5c62d518a..00000000000
--- a/server/sonar-web/src/main/js/apps/overview/coverage/main.js
+++ /dev/null
@@ -1,184 +0,0 @@
-import _ from 'underscore';
-import d3 from 'd3';
-import React from 'react';
-
-import { getMeasuresAndVariations } from '../../../api/measures';
-import { DetailedMeasure } from '../common-components';
-import { DomainTimeline } from '../timeline/domain-timeline';
-import { DomainTreemap } from '../domain/treemap';
-import { DomainBubbleChart } from '../domain/bubble-chart';
-import { getPeriodLabel, getPeriodDate } from './../helpers/period-label';
-import { TooltipsMixin } from '../../../components/mixins/tooltips-mixin';
-import { filterMetrics, filterMetricsForDomains } from '../helpers/metrics';
-import { Legend } from '../common-components';
-import { CHART_COLORS_RANGE_PERCENT } from '../../../helpers/constants';
-import { formatMeasure, formatMeasureVariation, localizeMetric } from '../../../helpers/measures';
-import { DonutChart } from '../../../components/charts/donut-chart';
-import DrilldownLink from '../helpers/drilldown-link';
-import { CoverageMeasure } from './coverage-measure';
-
-
-const UT_COVERAGE_METRICS = ['coverage', 'new_coverage', 'branch_coverage', 'line_coverage', 'uncovered_conditions',
- 'uncovered_lines'];
-const IT_COVERAGE_METRICS = ['it_coverage', 'new_it_coverage', 'it_branch_coverage', 'it_line_coverage',
- 'it_uncovered_conditions', 'it_uncovered_lines'];
-const OVERALL_COVERAGE_METRICS = ['overall_coverage', 'new_overall_coverage', 'overall_branch_coverage',
- 'overall_line_coverage', 'overall_uncovered_conditions', 'overall_uncovered_lines'];
-const TEST_METRICS = ['tests', 'test_execution_time', 'test_errors', 'test_failures', 'skipped_tests',
- 'test_success_density'];
-const KNOWN_METRICS = [].concat(TEST_METRICS, OVERALL_COVERAGE_METRICS, UT_COVERAGE_METRICS, IT_COVERAGE_METRICS);
-
-
-export const CoverageMain = React.createClass({
- mixins: [TooltipsMixin],
-
- getInitialState() {
- return {
- ready: false,
- leakPeriodLabel: getPeriodLabel(this.props.component.periods, this.props.leakPeriodIndex),
- leakPeriodDate: getPeriodDate(this.props.component.periods, this.props.leakPeriodIndex)
- };
- },
-
- componentDidMount() {
- this.requestMeasures().then(r => {
- let measures = this.getMeasuresValues(r, 'value');
- let leak = this.getMeasuresValues(r, 'var' + this.props.leakPeriodIndex);
- this.setState({ ready: true, measures, leak });
- });
- },
-
- getMeasuresValues (measures, fieldKey) {
- let values = {};
- Object.keys(measures).forEach(measureKey => {
- values[measureKey] = measures[measureKey][fieldKey];
- });
- return values;
- },
-
- getMetricsForDomain() {
- return this.props.metrics
- .filter(metric => ['Tests', 'Tests (Integration)', 'Tests (Overall)'].indexOf(metric.domain) !== -1)
- .map(metric => metric.key);
- },
-
- getMetricsForTimeline() {
- return filterMetricsForDomains(this.props.metrics, ['Tests', 'Tests (Integration)', 'Tests (Overall)']);
- },
-
- getAllMetricsForTimeline() {
- return filterMetrics(this.props.metrics);
- },
-
- requestMeasures () {
- return getMeasuresAndVariations(this.props.component.key, this.getMetricsForDomain());
- },
-
- renderLoading () {
- return <div className="text-center">
- <i className="spinner spinner-margin"/>
- </div>;
- },
-
- renderLegend () {
- return <Legend leakPeriodDate={this.state.leakPeriodDate} leakPeriodLabel={this.state.leakPeriodLabel}/>;
- },
-
- renderOtherMeasures() {
- let metrics = filterMetricsForDomains(this.props.metrics, ['Tests', 'Tests (Integration)', 'Tests (Overall)'])
- .filter(metric => KNOWN_METRICS.indexOf(metric.key) === -1)
- .map(metric => metric.key);
- return this.renderListOfMeasures(metrics);
- },
-
- renderUTCoverage () {
- let hasBothTypes = this.state.measures['coverage'] != null && this.state.measures['it_coverage'] != null;
- if (!hasBothTypes) {
- return null;
- }
- return <div className="overview-detailed-measures-list">
- <CoverageMeasure {...this.props} {...this.state} metric="coverage" leakMetric="new_coverage" type="PERCENT"/>
- <CoverageMeasure {...this.props} {...this.state} metric="line_coverage" leakMetric="new_line_coverage" type="PERCENT"/>
- <CoverageMeasure {...this.props} {...this.state} metric="branch_coverage" leakMetric="new_branch_coverage" type="PERCENT"/>
-
- <CoverageMeasure {...this.props} {...this.state} metric="uncovered_lines" type="INT"/>
- <CoverageMeasure {...this.props} {...this.state} metric="uncovered_conditions" type="INT"/>
- </div>;
- },
-
- renderITCoverage () {
- let hasBothTypes = this.state.measures['coverage'] != null && this.state.measures['it_coverage'] != null;
- if (!hasBothTypes) {
- return null;
- }
- return <div className="overview-detailed-measures-list">
- <CoverageMeasure {...this.props} {...this.state} metric="it_coverage" leakMetric="new_it_coverage" type="PERCENT"/>
- <CoverageMeasure {...this.props} {...this.state} metric="it_line_coverage" leakMetric="new_it_line_coverage" type="PERCENT"/>
- <CoverageMeasure {...this.props} {...this.state} metric="it_branch_coverage" leakMetric="new_it_branch_coverage" type="PERCENT"/>
-
- <CoverageMeasure {...this.props} {...this.state} metric="it_uncovered_lines" type="INT"/>
- <CoverageMeasure {...this.props} {...this.state} metric="it_uncovered_conditions" type="INT"/>
- </div>;
- },
-
- renderOverallCoverage () {
- return <div className="overview-detailed-measures-list">
- <CoverageMeasure {...this.props} {...this.state} metric="overall_coverage" leakMetric="new_overall_coverage" type="PERCENT"/>
- <CoverageMeasure {...this.props} {...this.state} metric="overall_line_coverage" leakMetric="new_overall_line_coverage" type="PERCENT"/>
- <CoverageMeasure {...this.props} {...this.state} metric="overall_branch_coverage" leakMetric="new_overall_branch_coverage" type="PERCENT"/>
-
- <CoverageMeasure {...this.props} {...this.state} metric="overall_uncovered_lines" type="INT"/>
- <CoverageMeasure {...this.props} {...this.state} metric="overall_uncovered_conditions" type="INT"/>
- </div>;
- },
-
- renderListOfMeasures(list) {
- let metrics = list
- .map(key => _.findWhere(this.props.metrics, { key }))
- .map(metric => {
- return <DetailedMeasure key={metric.key} {...this.props} {...this.state} metric={metric.key}
- type={metric.type}/>;
- });
- return <div className="overview-detailed-measures-list">{metrics}</div>;
- },
-
- render () {
- if (!this.state.ready) {
- return this.renderLoading();
- }
- let treemapScale = d3.scale.linear()
- .domain([0, 100])
- .range(CHART_COLORS_RANGE_PERCENT);
- return <div className="overview-detailed-page">
- <div className="overview-domain-charts">
- <div className="overview-domain overview-domain-fixed-width">
- <div className="overview-domain-header">
- <div className="overview-title">Tests Overview</div>
- {this.renderLegend()}
- </div>
- {this.renderOverallCoverage()}
- {this.renderUTCoverage()}
- {this.renderITCoverage()}
- {this.renderListOfMeasures(TEST_METRICS)}
- {this.renderOtherMeasures()}
- </div>
- <DomainBubbleChart {...this.props}
- xMetric="complexity"
- yMetric="overall_coverage"
- sizeMetrics={['overall_uncovered_lines']}/>
- </div>
-
- <div className="overview-domain-charts">
- <DomainTimeline {...this.props} {...this.state}
- initialMetric="overall_coverage"
- metrics={this.getMetricsForTimeline()}
- allMetrics={this.getAllMetricsForTimeline()}/>
- <DomainTreemap {...this.props}
- sizeMetric="ncloc"
- colorMetric="overall_coverage"
- scale={treemapScale}/>
- </div>
- </div>;
-
- }
-});
diff --git a/server/sonar-web/src/main/js/apps/overview/domain/header.js b/server/sonar-web/src/main/js/apps/overview/domain/header.js
deleted file mode 100644
index 16af08d9d13..00000000000
--- a/server/sonar-web/src/main/js/apps/overview/domain/header.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import React from 'react';
-
-export class DomainHeader extends React.Component {
- render () {
- return <h2 className="overview-title">{this.props.title}</h2>;
- }
-}
diff --git a/server/sonar-web/src/main/js/apps/overview/domain/measures-list.js b/server/sonar-web/src/main/js/apps/overview/domain/measures-list.js
deleted file mode 100644
index d37d4b97dd9..00000000000
--- a/server/sonar-web/src/main/js/apps/overview/domain/measures-list.js
+++ /dev/null
@@ -1,57 +0,0 @@
-import _ from 'underscore';
-import React from 'react';
-
-import DrilldownLink from '../helpers/drilldown-link';
-import { getMeasures } from '../../../api/measures';
-import { formatMeasure } from '../../../helpers/measures';
-
-
-export class DomainMeasuresList extends React.Component {
- constructor () {
- super();
- this.state = { measures: {} };
- }
-
- componentDidMount () {
- this.requestDetails();
- }
-
- requestDetails () {
- return getMeasures(this.props.component.key, this.props.metricsToDisplay).then(measures => {
- this.setState({ measures });
- });
- }
-
- getMetricObject (metricKey) {
- return _.findWhere(this.props.metrics, { key: metricKey });
- }
-
- renderValue (value, metricKey, metricType) {
- if (value != null) {
- return <DrilldownLink component={this.props.component.key} metric={metricKey}>
- {formatMeasure(value, metricType)}
- </DrilldownLink>;
- } else {
- return '—';
- }
- }
-
- render () {
- let rows = this.props.metricsToDisplay.map(metric => {
- let metricObject = this.getMetricObject(metric);
- return <tr key={metric}>
- <td>{metricObject.name}</td>
- <td className="thin nowrap text-right">
- {this.renderValue(this.state.measures[metric], metric, metricObject.type)}
- </td>
- </tr>;
- });
- return <table className="data zebra">
- <tbody>{rows}</tbody>
- </table>;
- }
-}
-
-DomainMeasuresList.propTypes = {
- metricsToDisplay: React.PropTypes.arrayOf(React.PropTypes.string).isRequired
-};
diff --git a/server/sonar-web/src/main/js/apps/overview/domain/timeline.js b/server/sonar-web/src/main/js/apps/overview/domain/timeline.js
deleted file mode 100644
index 9e5f5f8a47c..00000000000
--- a/server/sonar-web/src/main/js/apps/overview/domain/timeline.js
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
-
-
-
diff --git a/server/sonar-web/src/main/js/apps/overview/domains/coverage-domain.js b/server/sonar-web/src/main/js/apps/overview/domains/coverage-domain.js
new file mode 100644
index 00000000000..6df04be61bb
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/overview/domains/coverage-domain.js
@@ -0,0 +1,119 @@
+import d3 from 'd3';
+import React from 'react';
+
+import { getMeasuresAndVariations } from '../../../api/measures';
+import { DomainTimeline } from '../components/domain-timeline';
+import { DomainTreemap } from '../components/domain-treemap';
+import { DomainBubbleChart } from '../components/domain-bubble-chart';
+import { getPeriodLabel, getPeriodDate } from './../helpers/periods';
+import { TooltipsMixin } from '../../../components/mixins/tooltips-mixin';
+import { filterMetrics, filterMetricsForDomains } from '../helpers/metrics';
+import { Legend } from '../components/legend';
+import { CHART_COLORS_RANGE_PERCENT } from '../../../helpers/constants';
+import { CoverageMeasuresList } from '../components/coverage-measures-list';
+
+
+const TEST_DOMAINS = ['Tests', 'Tests (Integration)', 'Tests (Overall)'];
+
+
+export const CoverageMain = React.createClass({
+ mixins: [TooltipsMixin],
+
+ getInitialState() {
+ return {
+ ready: false,
+ leakPeriodLabel: getPeriodLabel(this.props.component.periods, this.props.leakPeriodIndex),
+ leakPeriodDate: getPeriodDate(this.props.component.periods, this.props.leakPeriodIndex)
+ };
+ },
+
+ componentDidMount() {
+ this.requestMeasures().then(r => {
+ let measures = this.getMeasuresValues(r, 'value');
+ let leak = this.getMeasuresValues(r, 'var' + this.props.leakPeriodIndex);
+ this.setState({ ready: true, measures, leak });
+ });
+ },
+
+ getMeasuresValues (measures, fieldKey) {
+ let values = {};
+ Object.keys(measures).forEach(measureKey => {
+ values[measureKey] = measures[measureKey][fieldKey];
+ });
+ return values;
+ },
+
+ getMetricsForDomain() {
+ return this.props.metrics
+ .filter(metric => TEST_DOMAINS.indexOf(metric.domain) !== -1)
+ .map(metric => metric.key);
+ },
+
+ getMetricsForTimeline() {
+ return filterMetricsForDomains(this.props.metrics, TEST_DOMAINS);
+ },
+
+ getAllMetricsForTimeline() {
+ return filterMetrics(this.props.metrics);
+ },
+
+ requestMeasures () {
+ return getMeasuresAndVariations(this.props.component.key, this.getMetricsForDomain());
+ },
+
+ renderLoading () {
+ return <div className="text-center">
+ <i className="spinner spinner-margin"/>
+ </div>;
+ },
+
+ renderLegend () {
+ return <Legend leakPeriodDate={this.state.leakPeriodDate} leakPeriodLabel={this.state.leakPeriodLabel}/>;
+ },
+
+ render () {
+ if (!this.state.ready) {
+ return this.renderLoading();
+ }
+
+ let treemapScale = d3.scale.linear()
+ .domain([0, 100])
+ .range(CHART_COLORS_RANGE_PERCENT);
+
+ return <div className="overview-detailed-page">
+ <div className="overview-cards-list">
+ <div className="overview-card overview-card-fixed-width">
+ <div className="overview-card-header">
+ <div className="overview-title">Coverage Overview</div>
+ {this.renderLegend()}
+ </div>
+ <CoverageMeasuresList {...this.props} {...this.state}/>
+ </div>
+
+ <div className="overview-card">
+ <DomainBubbleChart {...this.props}
+ xMetric="complexity"
+ yMetric="overall_coverage"
+ sizeMetrics={['overall_uncovered_lines']}/>
+ </div>
+ </div>
+
+ <div className="overview-cards-list">
+ <div className="overview-card">
+ <DomainTimeline {...this.props} {...this.state}
+ initialMetric="overall_coverage"
+ metrics={this.getMetricsForTimeline()}
+ allMetrics={this.getAllMetricsForTimeline()}/>
+ </div>
+
+ <div className="overview-card">
+ <DomainTreemap {...this.props}
+ sizeMetric="ncloc"
+ colorMetric="overall_coverage"
+ scale={treemapScale}/>
+ </div>
+ </div>
+ </div>;
+
+ }
+});
diff --git a/server/sonar-web/src/main/js/apps/overview/issues/main.js b/server/sonar-web/src/main/js/apps/overview/domains/debt-domain.js
index d5fd68c110a..bf7a73b1779 100644
--- a/server/sonar-web/src/main/js/apps/overview/issues/main.js
+++ b/server/sonar-web/src/main/js/apps/overview/domains/debt-domain.js
@@ -3,26 +3,22 @@ import d3 from 'd3';
import React from 'react';
import { getMeasuresAndVariations } from '../../../api/measures';
-import { DetailedMeasure } from '../common-components';
-import { DomainTimeline } from '../timeline/domain-timeline';
-import { DomainTreemap } from '../domain/treemap';
-import { DomainBubbleChart } from '../domain/bubble-chart';
-import { getPeriodLabel, getPeriodDate } from './../helpers/period-label';
+import { DetailedMeasure } from '../components/detailed-measure';
+import { DomainTimeline } from '../components/domain-timeline';
+import { DomainTreemap } from '../components/domain-treemap';
+import { DomainBubbleChart } from '../components/domain-bubble-chart';
+import { getPeriodLabel, getPeriodDate } from './../helpers/periods';
import { TooltipsMixin } from '../../../components/mixins/tooltips-mixin';
import { filterMetrics, filterMetricsForDomains } from '../helpers/metrics';
-import { Legend } from '../common-components';
+import { Legend } from '../components/legend';
import { CHART_COLORS_RANGE_PERCENT } from '../../../helpers/constants';
-import { IssueMeasure, AddedRemovedMeasure, OnNewCodeMeasure, SeverityMeasure } from './issue-measure';
-import { formatMeasure, formatMeasureVariation, localizeMetric } from '../../../helpers/measures';
-import IssuesLink from '../helpers/issues-link';
-import { Measure } from '../main/components';
-import { getMetricName } from '../helpers/metrics';
-import Tags from './tags';
-import Assignees from './assignees';
+import { AddedRemovedMeasure, AddedRemovedDebt, OnNewCodeMeasure, SeverityMeasure } from './../components/issue-measure';
+import { IssuesTags } from './../components/issues-tags';
+import Assignees from './../components/issues-assignees';
import { getFacets, extractAssignees } from '../../../api/issues';
import StatusHelper from '../../../components/shared/status-helper';
-import Rating from '../helpers/rating';
-import DrilldownLink from '../helpers/drilldown-link';
+import { Rating } from '../../../components/shared/rating';
+import { DrilldownLink } from '../../../components/shared/drilldown-link';
const KNOWN_METRICS = ['violations', 'sqale_index', 'sqale_rating', 'sqale_debt_ratio', 'blocker_violations',
@@ -119,20 +115,17 @@ export const IssuesMain = React.createClass({
.domain([1, 2, 3, 4, 5])
.range(CHART_COLORS_RANGE_PERCENT);
- let rating = formatMeasure(this.state.measures['sqale_rating'], 'RATING');
-
return <div className="overview-detailed-page">
- <div className="overview-domain-charts">
- <div className="overview-domain overview-domain-fixed-width">
- <div className="overview-domain-header">
+ <div className="overview-cards-list">
+ <div className="overview-card overview-card-fixed-width">
+ <div className="overview-card-header">
<div className="overview-title">Technical Debt Overview</div>
{this.renderLegend()}
</div>
<div className="overview-detailed-measures-list overview-detailed-measures-list-inline">
- <div className="overview-detailed-measure">
+ <div className="overview-detailed-measure overview-detailed-measure-rating">
<div className="overview-detailed-measure-nutshell">
- <span className="overview-detailed-measure-name">SQALE Rating</span>
<span className="overview-detailed-measure-value">
<DrilldownLink component={this.props.component.key} metric="sqale_rating">
<Rating value={this.state.measures['sqale_rating']}/>
@@ -142,7 +135,7 @@ export const IssuesMain = React.createClass({
</div>
<AddedRemovedMeasure {...this.props} {...this.state}
metric="violations" leakMetric="new_violations" type="INT"/>
- <AddedRemovedMeasure {...this.props} {...this.state}
+ <AddedRemovedDebt {...this.props} {...this.state}
metric="sqale_index" leakMetric="new_technical_debt" type="WORK_DUR"/>
<OnNewCodeMeasure {...this.props} {...this.state}
metric="sqale_debt_ratio" leakMetric="new_sqale_debt_ratio" type="PERCENT"/>
@@ -159,7 +152,7 @@ export const IssuesMain = React.createClass({
<div className="overview-detailed-measures-list overview-detailed-measures-list-inline">
<div className="overview-detailed-measure">
<div className="overview-detailed-measure-nutshell">
- <Tags {...this.props} tags={this.state.tags}/>
+ <IssuesTags {...this.props} tags={this.state.tags}/>
</div>
</div>
<div className="overview-detailed-measure">
@@ -178,21 +171,28 @@ export const IssuesMain = React.createClass({
{this.renderOtherMeasures()}
</div>
</div>
- <DomainBubbleChart {...this.props}
- xMetric="violations"
- yMetric="sqale_index"
- sizeMetrics={['blocker_violations', 'critical_violations']}/>
+
+ <div className="overview-card">
+ <DomainBubbleChart {...this.props}
+ xMetric="violations"
+ yMetric="sqale_index"
+ sizeMetrics={['blocker_violations', 'critical_violations']}/>
+ </div>
</div>
- <div className="overview-domain-charts">
- <DomainTimeline {...this.props} {...this.state}
- initialMetric="sqale_index"
- metrics={this.getMetricsForTimeline()}
- allMetrics={this.getAllMetricsForTimeline()}/>
- <DomainTreemap {...this.props}
- sizeMetric="ncloc"
- colorMetric="sqale_rating"
- scale={treemapScale}/>
+ <div className="overview-cards-list">
+ <div className="overview-card">
+ <DomainTimeline {...this.props} {...this.state}
+ initialMetric="sqale_index"
+ metrics={this.getMetricsForTimeline()}
+ allMetrics={this.getAllMetricsForTimeline()}/>
+ </div>
+ <div className="overview-card">
+ <DomainTreemap {...this.props}
+ sizeMetric="ncloc"
+ colorMetric="sqale_rating"
+ scale={treemapScale}/>
+ </div>
</div>
</div>;
diff --git a/server/sonar-web/src/main/js/apps/overview/duplications/main.js b/server/sonar-web/src/main/js/apps/overview/domains/duplications-domain.js
index d85d3790f87..e1e3977e264 100644
--- a/server/sonar-web/src/main/js/apps/overview/duplications/main.js
+++ b/server/sonar-web/src/main/js/apps/overview/domains/duplications-domain.js
@@ -2,14 +2,14 @@ import d3 from 'd3';
import React from 'react';
import { getMeasuresAndVariations } from '../../../api/measures';
-import { DetailedMeasure } from '../common-components';
-import { DomainTimeline } from '../timeline/domain-timeline';
-import { DomainTreemap } from '../domain/treemap';
-import { DomainBubbleChart } from '../domain/bubble-chart';
-import { getPeriodLabel, getPeriodDate } from './../helpers/period-label';
+import { DetailedMeasure } from '../components/detailed-measure';
+import { DomainTimeline } from '../components/domain-timeline';
+import { DomainTreemap } from '../components/domain-treemap';
+import { DomainBubbleChart } from '../components/domain-bubble-chart';
+import { getPeriodLabel, getPeriodDate } from './../helpers/periods';
import { TooltipsMixin } from '../../../components/mixins/tooltips-mixin';
import { filterMetrics, filterMetricsForDomains } from '../helpers/metrics';
-import { Legend } from '../common-components';
+import { Legend } from '../components/legend';
import { CHART_COLORS_RANGE_PERCENT } from '../../../helpers/constants';
@@ -85,9 +85,9 @@ export const DuplicationsMain = React.createClass({
.domain([0, 100])
.range(CHART_COLORS_RANGE_PERCENT);
return <div className="overview-detailed-page">
- <div className="overview-domain-charts">
- <div className="overview-domain overview-domain-fixed-width">
- <div className="overview-domain-header">
+ <div className="overview-cards-list">
+ <div className="overview-card overview-card-fixed-width">
+ <div className="overview-card-header">
<div className="overview-title">Duplications Overview</div>
{this.renderLegend()}
</div>
@@ -95,21 +95,27 @@ export const DuplicationsMain = React.createClass({
{this.renderMeasures()}
</div>
</div>
- <DomainBubbleChart {...this.props}
- xMetric="ncloc"
- yMetric="duplicated_lines"
- sizeMetrics={['duplicated_blocks']}/>
+ <div className="overview-card">
+ <DomainBubbleChart {...this.props}
+ xMetric="ncloc"
+ yMetric="duplicated_lines"
+ sizeMetrics={['duplicated_blocks']}/>
+ </div>
</div>
- <div className="overview-domain-charts">
- <DomainTimeline {...this.props} {...this.state}
- initialMetric="duplicated_lines_density"
- metrics={this.getMetricsForTimeline()}
- allMetrics={this.getAllMetricsForTimeline()}/>
- <DomainTreemap {...this.props}
- sizeMetric="ncloc"
- colorMetric="duplicated_lines_density"
- scale={treemapScale}/>
+ <div className="overview-cards-list">
+ <div className="overview-card">
+ <DomainTimeline {...this.props} {...this.state}
+ initialMetric="duplicated_lines_density"
+ metrics={this.getMetricsForTimeline()}
+ allMetrics={this.getAllMetricsForTimeline()}/>
+ </div>
+ <div className="overview-card">
+ <DomainTreemap {...this.props}
+ sizeMetric="ncloc"
+ colorMetric="duplicated_lines_density"
+ scale={treemapScale}/>
+ </div>
</div>
</div>;
diff --git a/server/sonar-web/src/main/js/apps/overview/size/main.js b/server/sonar-web/src/main/js/apps/overview/domains/size-domain.js
index d54f3894926..e65f0123578 100644
--- a/server/sonar-web/src/main/js/apps/overview/size/main.js
+++ b/server/sonar-web/src/main/js/apps/overview/domains/size-domain.js
@@ -1,16 +1,15 @@
import React from 'react';
-import { DomainLeakTitle } from '../main/components';
-import { LanguageDistribution } from './language-distribution';
-import { ComplexityDistribution } from './complexity-distribution';
+import { LanguageDistribution } from './../components/language-distribution';
+import { ComplexityDistribution } from './../components/complexity-distribution';
import { getMeasuresAndVariations } from '../../../api/measures';
-import { DetailedMeasure } from '../common-components';
-import { DomainTimeline } from '../timeline/domain-timeline';
-import { DomainTreemap } from '../domain/treemap';
-import { getPeriodLabel, getPeriodDate } from './../helpers/period-label';
+import { DetailedMeasure } from '../components/detailed-measure';
+import { DomainTimeline } from '../components/domain-timeline';
+import { DomainTreemap } from '../components/domain-treemap';
+import { getPeriodLabel, getPeriodDate } from './../helpers/periods';
import { TooltipsMixin } from '../../../components/mixins/tooltips-mixin';
import { filterMetrics, filterMetricsForDomains } from '../helpers/metrics';
-import { Legend } from '../common-components';
+import { Legend } from '../components/legend';
export const SizeMain = React.createClass({
@@ -96,8 +95,8 @@ export const SizeMain = React.createClass({
return this.renderLoading();
}
return <div className="overview-detailed-page">
- <div className="overview-domain">
- <div className="overview-domain-header">
+ <div className="overview-card">
+ <div className="overview-card-header">
<div className="overview-title">Size Overview</div>
{this.renderLegend()}
</div>
@@ -135,12 +134,16 @@ export const SizeMain = React.createClass({
</div>
</div>
- <div className="overview-domain-charts">
- <DomainTimeline {...this.props} {...this.state}
- initialMetric="ncloc"
- metrics={this.getMetricsForTimeline()}
- allMetrics={this.getAllMetricsForTimeline()}/>
- <DomainTreemap {...this.props} sizeMetric="ncloc"/>
+ <div className="overview-cards-list">
+ <div className="overview-card">
+ <DomainTimeline {...this.props} {...this.state}
+ initialMetric="ncloc"
+ metrics={this.getMetricsForTimeline()}
+ allMetrics={this.getAllMetricsForTimeline()}/>
+ </div>
+ <div className="overview-card">
+ <DomainTreemap {...this.props} sizeMetric="ncloc"/>
+ </div>
</div>
</div>;
diff --git a/server/sonar-web/src/main/js/apps/overview/main/gate/gate-condition.js b/server/sonar-web/src/main/js/apps/overview/gate/gate-condition.js
index 0819d6002c8..6d88348ed1a 100644
--- a/server/sonar-web/src/main/js/apps/overview/main/gate/gate-condition.js
+++ b/server/sonar-web/src/main/js/apps/overview/gate/gate-condition.js
@@ -1,8 +1,19 @@
import React from 'react';
-import Measure from '../../helpers/measure';
-import { getPeriodLabel, getPeriodDate } from '../../helpers/period-label';
-import DrilldownLink from '../../helpers/drilldown-link';
+import { getPeriodLabel, getPeriodDate } from '../helpers/periods';
+import { DrilldownLink } from '../../../components/shared/drilldown-link';
+import { formatMeasure } from '../../../helpers/measures';
+
+
+const Measure = React.createClass({
+ render() {
+ if (this.props.value == null || isNaN(this.props.value)) {
+ return null;
+ }
+ let formatted = formatMeasure(this.props.value, this.props.type);
+ return <span>{formatted}</span>;
+ }
+});
export default React.createClass({
diff --git a/server/sonar-web/src/main/js/apps/overview/main/gate/gate-conditions.js b/server/sonar-web/src/main/js/apps/overview/gate/gate-conditions.js
index adefecbded4..adefecbded4 100644
--- a/server/sonar-web/src/main/js/apps/overview/main/gate/gate-conditions.js
+++ b/server/sonar-web/src/main/js/apps/overview/gate/gate-conditions.js
diff --git a/server/sonar-web/src/main/js/apps/overview/main/gate/gate-empty.js b/server/sonar-web/src/main/js/apps/overview/gate/gate-empty.js
index 61347185593..61347185593 100644
--- a/server/sonar-web/src/main/js/apps/overview/main/gate/gate-empty.js
+++ b/server/sonar-web/src/main/js/apps/overview/gate/gate-empty.js
diff --git a/server/sonar-web/src/main/js/apps/overview/main/gate/gate.js b/server/sonar-web/src/main/js/apps/overview/gate/gate.js
index 076cbcd376a..076cbcd376a 100644
--- a/server/sonar-web/src/main/js/apps/overview/main/gate/gate.js
+++ b/server/sonar-web/src/main/js/apps/overview/gate/gate.js
diff --git a/server/sonar-web/src/main/js/apps/overview/helpers/donut.js b/server/sonar-web/src/main/js/apps/overview/helpers/donut.js
deleted file mode 100644
index 368a5222c64..00000000000
--- a/server/sonar-web/src/main/js/apps/overview/helpers/donut.js
+++ /dev/null
@@ -1,34 +0,0 @@
-import d3 from 'd3';
-import React from 'react';
-
-let Sector = React.createClass({
- render() {
- let arc = d3.svg.arc()
- .outerRadius(this.props.radius)
- .innerRadius(this.props.radius - this.props.thickness);
- return <path d={arc(this.props.data)} style={{ fill: this.props.fill }}/>;
- }
-});
-
-export default React.createClass({
- getDefaultProps() {
- return {
- size: 30,
- thickness: 6
- };
- },
-
- render() {
- let radius = this.props.size / 2;
- let pie = d3.layout.pie()
- .sort(null)
- .value(d => d.value);
- let data = this.props.data;
- let sectors = pie(data).map((d, i) => {
- return <Sector key={i} data={d} fill={data[i].fill} radius={radius} thickness={this.props.thickness}/>;
- });
- return <svg width={this.props.size} height={this.props.size}>
- <g transform={`translate(${radius}, ${radius})`}>{sectors}</g>
- </svg>;
- }
-});
diff --git a/server/sonar-web/src/main/js/apps/overview/helpers/measure-variation.js b/server/sonar-web/src/main/js/apps/overview/helpers/measure-variation.js
deleted file mode 100644
index e6ea091ce87..00000000000
--- a/server/sonar-web/src/main/js/apps/overview/helpers/measure-variation.js
+++ /dev/null
@@ -1,13 +0,0 @@
-import React from 'react';
-import { formatMeasureVariation } from '../../../helpers/measures';
-
-
-export default React.createClass({
- render() {
- if (this.props.value == null || isNaN(this.props.value)) {
- return null;
- }
- let formatted = formatMeasureVariation(this.props.value, this.props.type);
- return <span>{formatted}</span>;
- }
-});
diff --git a/server/sonar-web/src/main/js/apps/overview/helpers/measure.js b/server/sonar-web/src/main/js/apps/overview/helpers/measure.js
deleted file mode 100644
index 3d4eb5ec549..00000000000
--- a/server/sonar-web/src/main/js/apps/overview/helpers/measure.js
+++ /dev/null
@@ -1,12 +0,0 @@
-import React from 'react';
-import { formatMeasure } from '../../../helpers/measures';
-
-export default React.createClass({
- render() {
- if (this.props.value == null || isNaN(this.props.value)) {
- return null;
- }
- let formatted = formatMeasure(this.props.value, this.props.type);
- return <span>{formatted}</span>;
- }
-});
diff --git a/server/sonar-web/src/main/js/apps/overview/helpers/period-label.js b/server/sonar-web/src/main/js/apps/overview/helpers/periods.js
index 5b859894cd8..f4e38dc2098 100644
--- a/server/sonar-web/src/main/js/apps/overview/helpers/period-label.js
+++ b/server/sonar-web/src/main/js/apps/overview/helpers/periods.js
@@ -1,7 +1,8 @@
import _ from 'underscore';
import moment from 'moment';
-export let getPeriodLabel = (periods, periodIndex) => {
+
+export function getPeriodLabel (periods, periodIndex) {
let period = _.findWhere(periods, { index: periodIndex });
if (!period) {
return null;
@@ -10,12 +11,13 @@ export let getPeriodLabel = (periods, periodIndex) => {
return window.t('overview.period.previous_version_only_date');
}
return window.tp(`overview.period.${period.mode}`, period.modeParam);
-};
+}
+
-export let getPeriodDate = (periods, periodIndex) => {
+export function getPeriodDate (periods, periodIndex) {
let period = _.findWhere(periods, { index: periodIndex });
if (!period) {
return null;
}
return moment(period.date).toDate();
-};
+}
diff --git a/server/sonar-web/src/main/js/apps/overview/issues/severities.js b/server/sonar-web/src/main/js/apps/overview/issues/severities.js
deleted file mode 100644
index 26008e4648f..00000000000
--- a/server/sonar-web/src/main/js/apps/overview/issues/severities.js
+++ /dev/null
@@ -1,36 +0,0 @@
-import _ from 'underscore';
-import React from 'react';
-import SeverityHelper from '../../../components/shared/severity-helper';
-import { DomainHeader } from '../domain/header';
-import { getComponentIssuesUrl } from '../../../helpers/urls';
-import { formatMeasure } from '../../../helpers/measures';
-
-
-export default class extends React.Component {
- sortedSeverities () {
- return _.sortBy(this.props.severities, s => window.severityComparator(s.val));
- }
-
- render () {
- let rows = this.sortedSeverities().map(s => {
- let href = getComponentIssuesUrl(this.props.component.key, { resolved: 'false', severities: s.val });
- return <tr key={s.val}>
- <td>
- <SeverityHelper severity={s.val}/>
- </td>
- <td className="thin text-right">
- <a className="cell-link" href={href}>
- {formatMeasure(s.count, 'SHORT_INT')}
- </a>
- </td>
- </tr>;
- });
-
- return <div className="overview-domain-section">
- <DomainHeader title="Prioritized Issues"/>
- <table className="data zebra">
- <tbody>{rows}</tbody>
- </table>
- </div>;
- }
-}
diff --git a/server/sonar-web/src/main/js/apps/overview/main/components.js b/server/sonar-web/src/main/js/apps/overview/main/components.js
index b8bdf111c53..68a5cd0670d 100644
--- a/server/sonar-web/src/main/js/apps/overview/main/components.js
+++ b/server/sonar-web/src/main/js/apps/overview/main/components.js
@@ -3,12 +3,12 @@ import React from 'react';
import { Timeline } from './timeline';
import { navigate } from '../../../components/router/router';
-import { Legend } from '../common-components';
+import { Legend } from '../components/legend';
export const Domain = React.createClass({
render () {
- return <div className="overview-domain">{this.props.children}</div>;
+ return <div className="overview-card">{this.props.children}</div>;
}
});
@@ -50,7 +50,7 @@ export const DomainLeakTitle = React.createClass({
export const DomainHeader = React.createClass({
render () {
- return <div className="overview-domain-header">
+ return <div className="overview-card-header">
<DomainTitle linkTo={this.props.linkTo}>{this.props.title}</DomainTitle>
<Legend leakPeriodLabel={this.props.leakPeriodLabel} leakPeriodDate={this.props.leakPeriodDate}/>
</div>;
diff --git a/server/sonar-web/src/main/js/apps/overview/main/coverage.js b/server/sonar-web/src/main/js/apps/overview/main/coverage.js
index 056876f3e54..09752ad62a8 100644
--- a/server/sonar-web/src/main/js/apps/overview/main/coverage.js
+++ b/server/sonar-web/src/main/js/apps/overview/main/coverage.js
@@ -1,7 +1,7 @@
import React from 'react';
import { Domain, DomainHeader, DomainPanel, DomainNutshell, DomainLeak, MeasuresList, Measure, DomainMixin } from './components';
-import DrilldownLink from '../helpers/drilldown-link';
+import { DrilldownLink } from '../../../components/shared/drilldown-link';
import { TooltipsMixin } from '../../../components/mixins/tooltips-mixin';
import { getMetricName } from '../helpers/metrics';
import { formatMeasure } from '../../../helpers/measures';
diff --git a/server/sonar-web/src/main/js/apps/overview/main/duplications.js b/server/sonar-web/src/main/js/apps/overview/main/duplications.js
index db4c6e6c320..06583cf5f8e 100644
--- a/server/sonar-web/src/main/js/apps/overview/main/duplications.js
+++ b/server/sonar-web/src/main/js/apps/overview/main/duplications.js
@@ -1,7 +1,7 @@
import React from 'react';
import { Domain, DomainHeader, DomainPanel, DomainNutshell, DomainLeak, MeasuresList, Measure, DomainMixin } from './components';
-import DrilldownLink from '../helpers/drilldown-link';
+import { DrilldownLink } from '../../../components/shared/drilldown-link';
import { TooltipsMixin } from '../../../components/mixins/tooltips-mixin';
import { getMetricName } from '../helpers/metrics';
import { formatMeasure, formatMeasureVariation } from '../../../helpers/measures';
diff --git a/server/sonar-web/src/main/js/apps/overview/main/issues.js b/server/sonar-web/src/main/js/apps/overview/main/issues.js
index f281f85d08d..5a500cda8e4 100644
--- a/server/sonar-web/src/main/js/apps/overview/main/issues.js
+++ b/server/sonar-web/src/main/js/apps/overview/main/issues.js
@@ -2,16 +2,13 @@ import moment from 'moment';
import React from 'react';
import { Domain, DomainHeader, DomainPanel, DomainNutshell, DomainLeak, MeasuresList, Measure, DomainMixin } from './components';
-import Rating from './../helpers/rating';
-import IssuesLink from '../helpers/issues-link';
-import DrilldownLink from '../helpers/drilldown-link';
-import SeverityHelper from '../../../components/shared/severity-helper';
+import { Rating } from './../../../components/shared/rating';
+import { IssuesLink } from '../../../components/shared/issues-link';
+import { DrilldownLink } from '../../../components/shared/drilldown-link';
import SeverityIcon from '../../../components/shared/severity-icon';
-import StatusIcon from '../../../components/shared/status-icon';
import { TooltipsMixin } from '../../../components/mixins/tooltips-mixin';
import { getMetricName } from '../helpers/metrics';
-import { SEVERITIES } from '../../../helpers/constants';
-import { formatMeasure, formatMeasureVariation } from '../../../helpers/measures';
+import { formatMeasure } from '../../../helpers/measures';
export const GeneralIssues = React.createClass({
diff --git a/server/sonar-web/src/main/js/apps/overview/main/main.js b/server/sonar-web/src/main/js/apps/overview/main/main.js
index d23555cb0e0..993876b600c 100644
--- a/server/sonar-web/src/main/js/apps/overview/main/main.js
+++ b/server/sonar-web/src/main/js/apps/overview/main/main.js
@@ -6,7 +6,7 @@ import { GeneralIssues } from './issues';
import { GeneralCoverage } from './coverage';
import { GeneralDuplications } from './duplications';
import { GeneralSize } from './size';
-import { getPeriodLabel, getPeriodDate } from './../helpers/period-label';
+import { getPeriodLabel, getPeriodDate } from './../helpers/periods';
import { getMeasuresAndVariations } from '../../../api/measures';
import { getFacet, getIssuesCount } from '../../../api/issues';
import { getTimeMachineData } from '../../../api/time-machine';
@@ -157,7 +157,7 @@ export default React.createClass({
let props = _.extend({}, this.props, this.state);
- return <div className="overview-domains">
+ return <div className="overview-domains-list">
<GeneralIssues {...props} history={this.state.history['sqale_index']}/>
<GeneralCoverage {...props} history={this.state.history['overall_coverage']}/>
<GeneralDuplications {...props} history={this.state.history['duplicated_lines_density']}/>
diff --git a/server/sonar-web/src/main/js/apps/overview/main/size.js b/server/sonar-web/src/main/js/apps/overview/main/size.js
index 1c2f9fd6536..627a8569757 100644
--- a/server/sonar-web/src/main/js/apps/overview/main/size.js
+++ b/server/sonar-web/src/main/js/apps/overview/main/size.js
@@ -1,11 +1,11 @@
import React from 'react';
import { Domain, DomainHeader, DomainPanel, DomainNutshell, DomainLeak, MeasuresList, Measure, DomainMixin } from './components';
-import DrilldownLink from '../helpers/drilldown-link';
+import { DrilldownLink } from '../../../components/shared/drilldown-link';
import { TooltipsMixin } from '../../../components/mixins/tooltips-mixin';
import { getMetricName } from '../helpers/metrics';
import { formatMeasure, formatMeasureVariation } from '../../../helpers/measures';
-import { LanguageDistribution } from '../size/language-distribution';
+import { LanguageDistribution } from '../components/language-distribution';
export const GeneralSize = React.createClass({
diff --git a/server/sonar-web/src/main/js/apps/overview/meta.js b/server/sonar-web/src/main/js/apps/overview/meta.js
index e3e632a30fa..e6a48e71e09 100644
--- a/server/sonar-web/src/main/js/apps/overview/meta.js
+++ b/server/sonar-web/src/main/js/apps/overview/meta.js
@@ -1,7 +1,7 @@
import _ from 'underscore';
import React from 'react';
-import ProfileLink from './helpers/profile-link';
-import GateLink from './helpers/gate-link';
+import { QualityProfileLink } from './../../components/shared/quality-profile-link';
+import { QualityGateLink } from './../../components/shared/quality-gate-link';
export default React.createClass({
render() {
@@ -10,7 +10,7 @@ export default React.createClass({
return (
<li key={profile.key}>
<span className="note spacer-right">({profile.language})</span>
- <ProfileLink profile={profile.key}>{profile.name}</ProfileLink>
+ <QualityProfileLink profile={profile.key}>{profile.name}</QualityProfileLink>
</li>
);
}),
@@ -50,7 +50,7 @@ export default React.createClass({
<li>
{this.props.component.gate.isDefault ?
<span className="note spacer-right">(Default)</span> : null}
- <GateLink gate={this.props.component.gate.key}>{this.props.component.gate.name}</GateLink>
+ <QualityGateLink gate={this.props.component.gate.key}>{this.props.component.gate.name}</QualityGateLink>
</li>
</ul>
</div>
diff --git a/server/sonar-web/src/main/js/apps/overview/overview.js b/server/sonar-web/src/main/js/apps/overview/overview.js
index 8b36967694d..8301fd6add4 100644
--- a/server/sonar-web/src/main/js/apps/overview/overview.js
+++ b/server/sonar-web/src/main/js/apps/overview/overview.js
@@ -1,12 +1,12 @@
import React from 'react';
-import Gate from './main/gate/gate';
+import Gate from './gate/gate';
import GeneralMain from './main/main';
import Meta from './meta';
-import { SizeMain } from './size/main';
-import { DuplicationsMain } from './duplications/main';
-import { CoverageMain } from './coverage/main';
-import { IssuesMain } from './issues/main';
+import { SizeMain } from './domains/size-domain';
+import { DuplicationsMain } from './domains/duplications-domain';
+import { CoverageMain } from './domains/coverage-domain';
+import { IssuesMain } from './domains/debt-domain';
import { getMetrics } from '../../api/metrics';
import { RouterMixin } from '../../components/router/router';
diff --git a/server/sonar-web/src/main/js/apps/overview/helpers/drilldown-link.js b/server/sonar-web/src/main/js/components/shared/drilldown-link.js
index 51f18ea39f5..d149a3db83c 100644
--- a/server/sonar-web/src/main/js/apps/overview/helpers/drilldown-link.js
+++ b/server/sonar-web/src/main/js/components/shared/drilldown-link.js
@@ -1,29 +1,32 @@
import _ from 'underscore';
import moment from 'moment';
import React from 'react';
-import IssuesLink from './issues-link';
-import { getComponentDrilldownUrl } from '../../../helpers/urls';
+import { IssuesLink } from './issues-link';
+import { getComponentDrilldownUrl } from '../../helpers/urls';
-export default React.createClass({
+
+const ISSUE_MEASURES = [
+ 'violations',
+ 'blocker_violations',
+ 'critical_violations',
+ 'major_violations',
+ 'minor_violations',
+ 'info_violations',
+ 'new_blocker_violations',
+ 'new_critical_violations',
+ 'new_major_violations',
+ 'new_minor_violations',
+ 'new_info_violations',
+ 'open_issues',
+ 'reopened_issues',
+ 'confirmed_issues',
+ 'false_positive_issues'
+];
+
+
+export const DrilldownLink = React.createClass({
isIssueMeasure() {
- const ISSUE_MEASURES = [
- 'violations',
- 'blocker_violations',
- 'critical_violations',
- 'major_violations',
- 'minor_violations',
- 'info_violations',
- 'new_blocker_violations',
- 'new_critical_violations',
- 'new_major_violations',
- 'new_minor_violations',
- 'new_info_violations',
- 'open_issues',
- 'reopened_issues',
- 'confirmed_issues',
- 'false_positive_issues'
- ];
return ISSUE_MEASURES.indexOf(this.props.metric) !== -1;
},
@@ -83,6 +86,6 @@ export default React.createClass({
}
let url = getComponentDrilldownUrl(this.props.component, this.props.metric, this.props.period);
- return <a href={url}>{this.props.children}</a>;
+ return <a className={this.props.className} href={url}>{this.props.children}</a>;
}
});
diff --git a/server/sonar-web/src/main/js/apps/overview/helpers/issues-link.js b/server/sonar-web/src/main/js/components/shared/issues-link.js
index c3543f597e1..230ead7c83b 100644
--- a/server/sonar-web/src/main/js/apps/overview/helpers/issues-link.js
+++ b/server/sonar-web/src/main/js/components/shared/issues-link.js
@@ -1,8 +1,9 @@
import React from 'react';
-import { getComponentIssuesUrl } from '../../../helpers/urls';
+import { getComponentIssuesUrl } from '../../helpers/urls';
-export default React.createClass({
+
+export const IssuesLink = React.createClass({
render() {
let url = getComponentIssuesUrl(this.props.component, this.props.params);
return <a className={this.props.className} href={url}>{this.props.children}</a>;
diff --git a/server/sonar-web/src/main/js/apps/overview/helpers/gate-link.js b/server/sonar-web/src/main/js/components/shared/quality-gate-link.js
index 79878ac9fd6..5b05c8cd4af 100644
--- a/server/sonar-web/src/main/js/apps/overview/helpers/gate-link.js
+++ b/server/sonar-web/src/main/js/components/shared/quality-gate-link.js
@@ -1,6 +1,7 @@
import React from 'react';
-export default React.createClass({
+
+export const QualityGateLink = React.createClass({
render() {
let url = `${baseUrl}/quality_gates/show/${this.props.gate}`;
return <a href={url}>{this.props.children}</a>;
diff --git a/server/sonar-web/src/main/js/apps/overview/helpers/profile-link.js b/server/sonar-web/src/main/js/components/shared/quality-profile-link.js
index 22065abad56..c7c296bce47 100644
--- a/server/sonar-web/src/main/js/apps/overview/helpers/profile-link.js
+++ b/server/sonar-web/src/main/js/components/shared/quality-profile-link.js
@@ -1,6 +1,7 @@
import React from 'react';
-export default React.createClass({
+
+export const QualityProfileLink = React.createClass({
render() {
let url = `${baseUrl}/profiles/show?key=${encodeURIComponent(this.props.profile)}`;
return <a href={url}>{this.props.children}</a>;
diff --git a/server/sonar-web/src/main/js/apps/overview/helpers/rating.js b/server/sonar-web/src/main/js/components/shared/rating.js
index 5568dc5d2dc..791466fdfc4 100644
--- a/server/sonar-web/src/main/js/apps/overview/helpers/rating.js
+++ b/server/sonar-web/src/main/js/components/shared/rating.js
@@ -1,7 +1,9 @@
import React from 'react';
-import { formatMeasure } from '../../../helpers/measures';
-export default React.createClass({
+import { formatMeasure } from '../../helpers/measures';
+
+
+export const Rating = React.createClass({
render() {
if (this.props.value == null || isNaN(this.props.value)) {
return null;
diff --git a/server/sonar-web/src/main/less/init/misc.less b/server/sonar-web/src/main/less/init/misc.less
index bcd74fe3882..803bc92954c 100644
--- a/server/sonar-web/src/main/less/init/misc.less
+++ b/server/sonar-web/src/main/less/init/misc.less
@@ -100,6 +100,10 @@ td.big-spacer-top { padding-top: 16px; }
flex: 1;
}
+.space-between {
+ justify-content: space-between !important;
+}
+
// Background Color
diff --git a/server/sonar-web/src/main/less/pages/overview.less b/server/sonar-web/src/main/less/pages/overview.less
index 99a414b5448..972b04cbee0 100644
--- a/server/sonar-web/src/main/less/pages/overview.less
+++ b/server/sonar-web/src/main/less/pages/overview.less
@@ -3,8 +3,6 @@
@import (reference) "../init/type";
@import (reference) "../init/links";
-@side-padding: 20px;
-
.overview {
display: flex;
flex-wrap: wrap;
@@ -32,7 +30,7 @@
background-color: @barBackgroundColor;
.overview-title {
- margin: 0 @side-padding;
+ margin: 0 20px;
}
}
@@ -42,11 +40,7 @@
}
.overview-gate-condition {
- padding: 10px @side-padding;
-}
-
-.overview-gate-condition-metric {
-
+ padding: 10px 20px;
}
.overview-gate-condition-value {
@@ -56,7 +50,7 @@
}
.overview-gate-warning {
- margin: 15px @side-padding 0;
+ margin: 15px 20px 0;
}
/*
@@ -78,16 +72,6 @@
}
/*
- * Cards
- * TODO drop it
- */
-
-.overview-cards {
- display: flex;
- flex-wrap: wrap;
-}
-
-/*
* Meta
*/
@@ -99,7 +83,7 @@
.overview-meta-card {
min-width: 200px;
- padding: @side-padding;
+ padding: 20px;
box-sizing: border-box;
}
@@ -122,19 +106,28 @@
* Domain
*/
-.overview-domains {
+.overview-domains-list {
animation: fadeIn 0.5s forwards;
}
-.overview-domain {
- margin: 30px @side-padding;
+.overview-cards-list {
+ display: flex;
+
+ & > .overview-card,
+ & > .overview-domain-chart {
+ flex: 1;
+ }
+}
+
+.overview-card {
+ margin: 30px 20px;
}
-.overview-domain-fixed-width {
+.overview-card-fixed-width {
max-width: 560px;
}
-.overview-domain-header {
+.overview-card-header {
display: flex;
align-items: baseline;
justify-content: space-between;
@@ -205,7 +198,6 @@
}
.overview-domain-measure {
-
}
.overview-domain-measure-value {
@@ -268,6 +260,7 @@
margin-bottom: 8px;
flex: 0 1 auto;
font-weight: 500;
+ text-align: center;
}
.overview-detailed-measure + .overview-detailed-measure {
@@ -297,6 +290,13 @@
background-color: #fff;
}
+.overview-detailed-measure-rating {
+ border: none !important;
+ background: none;
+
+ .overview-detailed-measure-value { font-size: 30px; }
+}
+
.overview-detailed-measure-nutshell,
.overview-detailed-measure-leak,
.overview-detailed-measure-chart {
@@ -364,15 +364,6 @@
* Charts
*/
-.overview-domain-charts {
- display: flex;
-
- & > .overview-domain,
- & > .overview-domain-chart {
- flex: 1;
- }
-}
-
.overview-domain-chart {
.overview-title {
display: inline-block;
@@ -380,28 +371,32 @@
}
}
+.overview-domain-chart + .overview-domain-chart {
+ margin-top: 60px;
+}
+
.overview-bar-chart {
width: 100%;
padding-top: 10px;
padding-bottom: 15px;
-}
-.bar-chart-bar {
- fill: @blue;
-}
+ .bar-chart-bar {
+ fill: @blue;
+ }
-.bar-chart-tick {
- fill: @secondFontColor;
- font-size: 11px;
- text-anchor: middle;
-}
+ .bar-chart-tick {
+ fill: @secondFontColor;
+ font-size: 11px;
+ text-anchor: middle;
+ }
-.histogram-tick {
- text-anchor: end;
-}
+ .histogram-tick {
+ text-anchor: end;
+ }
-.histogram-value {
- text-anchor: start;
+ .histogram-value {
+ text-anchor: start;
+ }
}
.overview-timeline {
@@ -510,7 +505,9 @@
cursor: pointer;
transition: all 0.2s ease;
- &:hover { fill-opacity: 0.8; }
+ &:hover {
+ fill-opacity: 0.8;
+ }
}
.bubble-chart-grid {
@@ -530,12 +527,29 @@
}
.overview-donut-chart {
- display: inline-block;
- vertical-align: top;
- margin-right: 8px;
+ position: relative;
+ text-align: center;
+
+ .overview-detailed-measure-value {
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ }
}
+/*
+ * Misc
+ */
+.overview-nutshell {
+ background-color: #fff;
+}
+
+.overview-leak {
+ background-color: #fffae7;
+}
/*
* Responsive Stuff
diff --git a/server/sonar-web/tests/apps/overview/components/complexity-distribution-test.js b/server/sonar-web/tests/apps/overview/components/complexity-distribution-test.js
new file mode 100644
index 00000000000..a79cdbf34bc
--- /dev/null
+++ b/server/sonar-web/tests/apps/overview/components/complexity-distribution-test.js
@@ -0,0 +1,41 @@
+import { expect } from 'chai';
+import React from 'react';
+import TestUtils from 'react-addons-test-utils';
+
+import { ComplexityDistribution } from '../../../../src/main/js/apps/overview/components/complexity-distribution';
+
+
+const DISTRIBUTION = '1=11950;2=86;4=77;6=43;8=17;10=12;12=3';
+
+
+describe('ComplexityDistribution', function () {
+ let props;
+
+ beforeEach(function () {
+ let renderer = TestUtils.createRenderer();
+ renderer.render(<ComplexityDistribution distribution={DISTRIBUTION}/>);
+ let output = renderer.getRenderOutput();
+ let child = React.Children.only(output.props.children);
+ props = child.props;
+ });
+
+ it('should pass right data', function () {
+ expect(props.data).to.deep.equal([
+ { x: 0, y: 11950, value: 1 },
+ { x: 1, y: 86, value: 2 },
+ { x: 2, y: 77, value: 4 },
+ { x: 3, y: 43, value: 6 },
+ { x: 4, y: 17, value: 8 },
+ { x: 5, y: 12, value: 10 },
+ { x: 6, y: 3, value: 12 }
+ ]);
+ });
+
+ it('should pass right xTicks', function () {
+ expect(props.xTicks).to.deep.equal([1, 2, 4, 6, 8, 10, 12]);
+ });
+
+ it('should pass right xValues', function () {
+ expect(props.xValues).to.deep.equal(['11,950', '86', '77', '43', '17', '12', '3']);
+ });
+});
diff --git a/server/sonar-web/tests/apps/overview/components/issues-tags-test.js b/server/sonar-web/tests/apps/overview/components/issues-tags-test.js
new file mode 100644
index 00000000000..67d90c7c11f
--- /dev/null
+++ b/server/sonar-web/tests/apps/overview/components/issues-tags-test.js
@@ -0,0 +1,45 @@
+import { expect } from 'chai';
+import React from 'react';
+import TestUtils from 'react-addons-test-utils';
+
+import { IssuesTags } from '../../../../src/main/js/apps/overview/components/issues-tags';
+import { WordCloud } from '../../../../src/main/js/components/charts/word-cloud';
+
+
+const COMPONENT = { key: 'component-key' };
+
+const TAGS = [
+ { val: 'first', count: 3 },
+ { val: 'second', count: 7000 },
+ { val: 'third', count: 2 }
+];
+
+
+describe('IssuesTags', function () {
+ it('should pass right data', function () {
+ let renderer = TestUtils.createRenderer();
+ renderer.render(<IssuesTags tags={TAGS} component={COMPONENT}/>);
+ let output = renderer.getRenderOutput();
+ expect(output.type).to.equal(WordCloud);
+ expect(output.props.items).to.deep.equal([
+ {
+ "link": '/component_issues?id=component-key#resolved=false|tags=first',
+ "size": 3,
+ "text": 'first',
+ "tooltip": 'Issues: 3'
+ },
+ {
+ "link": '/component_issues?id=component-key#resolved=false|tags=second',
+ "size": 7000,
+ "text": 'second',
+ "tooltip": 'Issues: 7k'
+ },
+ {
+ "link": '/component_issues?id=component-key#resolved=false|tags=third',
+ "size": 2,
+ "text": 'third',
+ "tooltip": 'Issues: 2'
+ }
+ ]);
+ });
+});
diff --git a/server/sonar-web/tests/apps/overview/components/language-distribution-test.js b/server/sonar-web/tests/apps/overview/components/language-distribution-test.js
new file mode 100644
index 00000000000..a54ba2b1260
--- /dev/null
+++ b/server/sonar-web/tests/apps/overview/components/language-distribution-test.js
@@ -0,0 +1,38 @@
+import { expect } from 'chai';
+import React from 'react';
+import TestUtils from 'react-addons-test-utils';
+
+import { LanguageDistribution } from '../../../../src/main/js/apps/overview/components/language-distribution';
+
+
+const DISTRIBUTION = '<null>=17345;java=194342;js=20984';
+const LINES = 1000000;
+
+
+describe('LanguageDistribution', function () {
+ let props;
+
+ beforeEach(function () {
+ let renderer = TestUtils.createRenderer();
+ renderer.render(<LanguageDistribution distribution={DISTRIBUTION} lines={LINES}/>);
+ let output = renderer.getRenderOutput();
+ let child = React.Children.only(output.props.children);
+ props = child.props;
+ });
+
+ it('should pass right data', function () {
+ expect(props.data).to.deep.equal([
+ { x: 194342, y: 1, value: 'java' },
+ { x: 20984, y: 2, value: 'js' },
+ { x: 17345, y: 0, value: '<null>' }
+ ]);
+ });
+
+ it('should pass right yTicks', function () {
+ expect(props.yTicks).to.deep.equal(['java', 'js', '<null>']);
+ });
+
+ it('should pass right yValues', function () {
+ expect(props.yValues).to.deep.equal(['19.4%', '2.1%', '1.7%']);
+ });
+});
diff --git a/server/sonar-web/tests/apps/overview/components/legend-test.js b/server/sonar-web/tests/apps/overview/components/legend-test.js
new file mode 100644
index 00000000000..d4c877eafd5
--- /dev/null
+++ b/server/sonar-web/tests/apps/overview/components/legend-test.js
@@ -0,0 +1,26 @@
+import { expect } from 'chai';
+import React from 'react';
+import TestUtils from 'react-addons-test-utils';
+
+import { Legend } from '../../../../src/main/js/apps/overview/components/legend';
+
+
+const DATE = new Date(2015, 3, 7);
+const LABEL = 'since 1.0';
+
+
+describe('Legend', function () {
+ it('should not render', function () {
+ let renderer = TestUtils.createRenderer();
+ renderer.render(<Legend/>);
+ let output = renderer.getRenderOutput();
+ expect(output).to.be.null;
+ });
+
+ it('should render', function () {
+ let renderer = TestUtils.createRenderer();
+ renderer.render(<Legend leakPeriodDate={DATE} leakPeriodLabel={LABEL}/>);
+ let output = renderer.getRenderOutput();
+ expect(output).to.not.be.null;
+ });
+});
diff --git a/server/sonar-web/tests/apps/overview/helpers/metrics-test.js b/server/sonar-web/tests/apps/overview/helpers/metrics-test.js
new file mode 100644
index 00000000000..e090ea54ed3
--- /dev/null
+++ b/server/sonar-web/tests/apps/overview/helpers/metrics-test.js
@@ -0,0 +1,110 @@
+import { expect } from 'chai';
+
+import { filterMetrics, filterMetricsForDomains, getShortType, getMetricName } from
+ '../../../../src/main/js/apps/overview/helpers/metrics';
+
+
+const METRICS = [
+ { key: 'normal_metric', type: 'INT', hidden: false },
+ { key: 'hidden_metric', type: 'INT', hidden: true },
+ { key: 'DATA_metric', type: 'DATA', hidden: false },
+ { key: 'DISTRIB_metric', type: 'DISTRIB', hidden: false },
+ { key: 'new_metric', type: 'FLOAT', hidden: false }
+];
+
+
+describe('Overview Helpers', function () {
+ describe('Metrics', function () {
+
+ describe('#filterMetrics', function () {
+ it('should filter out hidden metrics', function () {
+ let metrics = [
+ { key: 'normal_metric', type: 'INT', hidden: false },
+ { key: 'hidden_metric', type: 'INT', hidden: true }
+ ];
+ expect(filterMetrics(metrics)).to.have.length(1);
+ });
+
+ it('should filter out DATA and DISTRIB metrics', function () {
+ let metrics = [
+ { key: 'normal_metric', type: 'INT', hidden: false },
+ { key: 'DATA_metric', type: 'DATA', hidden: false },
+ { key: 'DISTRIB_metric', type: 'DISTRIB', hidden: false }
+ ];
+ expect(filterMetrics(metrics)).to.have.length(1);
+ });
+
+ it('should filter out differential metrics', function () {
+ let metrics = [
+ { key: 'normal_metric', type: 'INT', hidden: false },
+ { key: 'new_metric', type: 'FLOAT', hidden: false }
+ ];
+ expect(filterMetrics(metrics)).to.have.length(1);
+ });
+ });
+
+ describe('#filterMetricsForDomains', function () {
+ it('should filter out hidden metrics', function () {
+ let metrics = [
+ { key: 'normal_metric', type: 'INT', hidden: false, domain: 'first' },
+ { key: 'hidden_metric', type: 'INT', hidden: true, domain: 'first' }
+ ];
+ expect(filterMetricsForDomains(metrics, ['first'])).to.have.length(1);
+ });
+
+ it('should filter out DATA and DISTRIB metrics', function () {
+ let metrics = [
+ { key: 'normal_metric', type: 'INT', hidden: false, domain: 'first' },
+ { key: 'DATA_metric', type: 'DATA', hidden: false, domain: 'first' },
+ { key: 'DISTRIB_metric', type: 'DISTRIB', hidden: false, domain: 'first' }
+ ];
+ expect(filterMetricsForDomains(metrics, ['first'])).to.have.length(1);
+ });
+
+ it('should filter out differential metrics', function () {
+ let metrics = [
+ { key: 'normal_metric', type: 'INT', hidden: false, domain: 'first' },
+ { key: 'new_metric', type: 'FLOAT', hidden: false, domain: 'first' }
+ ];
+ expect(filterMetricsForDomains(metrics, ['first'])).to.have.length(1);
+ });
+
+ it('should filter metrics by domains', function () {
+ let metrics = [
+ { key: 'normal_metric', type: 'INT', hidden: false, domain: 'first' },
+ { key: 'normal_metric1', type: 'INT', hidden: false, domain: 'second' },
+ { key: 'normal_metric2', type: 'INT', hidden: false, domain: 'third' },
+ { key: 'normal_metric3', type: 'INT', hidden: false, domain: 'second' }
+ ];
+ expect(filterMetricsForDomains(metrics, ['first', 'second'])).to.have.length(3);
+ });
+ });
+
+
+ describe('#getShortType', function () {
+ it('should shorten INT', function () {
+ expect(getShortType('INT')).to.equal('SHORT_INT');
+ });
+
+ it('should shorten WORK_DUR', function () {
+ expect(getShortType('WORK_DUR')).to.equal('SHORT_WORK_DUR');
+ });
+
+ it('should not shorten FLOAT', function () {
+ expect(getShortType('FLOAT')).to.equal('FLOAT');
+ });
+
+ it('should not shorten PERCENT', function () {
+ expect(getShortType('PERCENT')).to.equal('PERCENT');
+ });
+ });
+
+
+ describe('#getMetricName', function () {
+ it('should return metric name', function () {
+ expect(getMetricName('metric_name')).to.equal('overview.metric.metric_name');
+ });
+ });
+
+ });
+});
diff --git a/server/sonar-web/tests/apps/overview/helpers/periods-test.js b/server/sonar-web/tests/apps/overview/helpers/periods-test.js
new file mode 100644
index 00000000000..6dcd1ff3f96
--- /dev/null
+++ b/server/sonar-web/tests/apps/overview/helpers/periods-test.js
@@ -0,0 +1,57 @@
+import chai from 'chai';
+import { expect } from 'chai';
+chai.use(require('chai-datetime'));
+
+import { getPeriodDate, getPeriodLabel } from '../../../../src/main/js/apps/overview/helpers/periods';
+
+
+const PERIOD = {
+ date: '2015-09-09T00:00:00+0200',
+ index: '1',
+ mode: 'previous_version',
+ modeParam: '1.7'
+};
+
+const PERIOD_WITHOUT_VERSION = {
+ date: '2015-09-09T00:00:00+0200',
+ index: '1',
+ mode: 'previous_version',
+ modeParam: ''
+};
+
+
+describe('Overview Helpers', function () {
+ describe('Periods', function () {
+
+ describe('#getPeriodDate', function () {
+ it('should return date', function () {
+ let result = getPeriodDate([PERIOD], PERIOD.index);
+ expect(result).to.equalDate(new Date(2015, 8, 9));
+ });
+
+ it('should return null', function () {
+ let result = getPeriodDate([], '1');
+ expect(result).to.be.null;
+ });
+ });
+
+
+ describe('#getPeriodLabel', function () {
+ it('should return label', function () {
+ let result = getPeriodLabel([PERIOD], PERIOD.index);
+ expect(result).to.equal('overview.period.previous_version.1.7');
+ });
+
+ it('should return "since previous version"', function () {
+ let result = getPeriodLabel([PERIOD_WITHOUT_VERSION], PERIOD_WITHOUT_VERSION.index);
+ expect(result).to.equal('overview.period.previous_version_only_date');
+ });
+
+ it('should return null', function () {
+ let result = getPeriodLabel([], '1');
+ expect(result).to.be.null;
+ });
+ });
+
+ });
+});
diff --git a/server/sonar-web/tests/apps/overview-test.js b/server/sonar-web/tests/apps/overview/overview-test.js
index 2bfea7f419a..2b7ae01d8be 100644
--- a/server/sonar-web/tests/apps/overview-test.js
+++ b/server/sonar-web/tests/apps/overview/overview-test.js
@@ -3,9 +3,9 @@ import ReactDOM from 'react-dom';
import TestUtils from 'react-addons-test-utils';
import { expect } from 'chai';
-import Gate from '../../src/main/js/apps/overview/main/gate/gate';
-import GateConditions from '../../src/main/js/apps/overview/main/gate/gate-conditions';
-import GateCondition from '../../src/main/js/apps/overview/main/gate/gate-condition';
+import Gate from '../../../src/main/js/apps/overview/gate/gate';
+import GateConditions from '../../../src/main/js/apps/overview/gate/gate-conditions';
+import GateCondition from '../../../src/main/js/apps/overview/gate/gate-condition';
describe('Overview', function () {
@@ -41,4 +41,11 @@ describe('Overview', function () {
});
});
+
+ describe('Helpers', function () {
+ describe('Periods', function () {
+
+ });
+ });
+
});