From 27c49eda28940ca7600587b75fcbc9223268ee8b Mon Sep 17 00:00:00 2001 From: Stas Vilchik Date: Thu, 12 Nov 2015 18:05:08 +0100 Subject: [PATCH] SONAR-6331 refactor overview code --- server/sonar-web/package.json | 1 + .../complexity-distribution.js | 10 +- .../overview/components/coverage-measure.js | 53 +++++ .../components/coverage-measures-list.js | 123 ++++++++++++ .../detailed-measure.js} | 20 +- .../domain-bubble-chart.js} | 5 +- .../domain-timeline.js | 5 +- .../domain-treemap.js} | 5 +- .../{issues => components}/issue-measure.js | 75 ++++++- .../issues-assignees.js} | 9 +- .../tags.js => components/issues-tags.js} | 12 +- .../language-distribution.js | 17 +- .../js/apps/overview/components/legend.js | 16 ++ .../timeline-chart.js | 0 .../overview/coverage/coverage-measure.js | 85 -------- .../main/js/apps/overview/coverage/main.js | 184 ------------------ .../main/js/apps/overview/domain/header.js | 7 - .../js/apps/overview/domain/measures-list.js | 57 ------ .../main/js/apps/overview/domain/timeline.js | 7 - .../apps/overview/domains/coverage-domain.js | 119 +++++++++++ .../main.js => domains/debt-domain.js} | 74 +++---- .../duplications-domain.js} | 50 ++--- .../{size/main.js => domains/size-domain.js} | 35 ++-- .../{main => }/gate/gate-condition.js | 17 +- .../{main => }/gate/gate-conditions.js | 0 .../overview/{main => }/gate/gate-empty.js | 0 .../js/apps/overview/{main => }/gate/gate.js | 0 .../main/js/apps/overview/helpers/donut.js | 34 ---- .../overview/helpers/measure-variation.js | 13 -- .../main/js/apps/overview/helpers/measure.js | 12 -- .../helpers/{period-label.js => periods.js} | 10 +- .../js/apps/overview/issues/severities.js | 36 ---- .../main/js/apps/overview/main/components.js | 6 +- .../main/js/apps/overview/main/coverage.js | 2 +- .../js/apps/overview/main/duplications.js | 2 +- .../src/main/js/apps/overview/main/issues.js | 11 +- .../src/main/js/apps/overview/main/main.js | 4 +- .../src/main/js/apps/overview/main/size.js | 4 +- .../src/main/js/apps/overview/meta.js | 8 +- .../src/main/js/apps/overview/overview.js | 10 +- .../shared}/drilldown-link.js | 45 +++-- .../shared}/issues-link.js | 5 +- .../shared/quality-gate-link.js} | 3 +- .../shared/quality-profile-link.js} | 3 +- .../helpers => components/shared}/rating.js | 6 +- server/sonar-web/src/main/less/init/misc.less | 4 + .../src/main/less/pages/overview.less | 120 +++++++----- .../complexity-distribution-test.js | 41 ++++ .../overview/components/issues-tags-test.js | 45 +++++ .../components/language-distribution-test.js | 38 ++++ .../apps/overview/components/legend-test.js | 26 +++ .../apps/overview/helpers/metrics-test.js | 110 +++++++++++ .../apps/overview/helpers/periods-test.js | 57 ++++++ .../apps/{ => overview}/overview-test.js | 13 +- 54 files changed, 979 insertions(+), 675 deletions(-) rename server/sonar-web/src/main/js/apps/overview/{size => components}/complexity-distribution.js (86%) create mode 100644 server/sonar-web/src/main/js/apps/overview/components/coverage-measure.js create mode 100644 server/sonar-web/src/main/js/apps/overview/components/coverage-measures-list.js rename server/sonar-web/src/main/js/apps/overview/{common-components.js => components/detailed-measure.js} (69%) rename server/sonar-web/src/main/js/apps/overview/{domain/bubble-chart.js => components/domain-bubble-chart.js} (97%) rename server/sonar-web/src/main/js/apps/overview/{timeline => components}/domain-timeline.js (98%) rename server/sonar-web/src/main/js/apps/overview/{domain/treemap.js => components/domain-treemap.js} (96%) rename server/sonar-web/src/main/js/apps/overview/{issues => components}/issue-measure.js (72%) rename server/sonar-web/src/main/js/apps/overview/{issues/assignees.js => components/issues-assignees.js} (80%) rename server/sonar-web/src/main/js/apps/overview/{issues/tags.js => components/issues-tags.js} (77%) rename server/sonar-web/src/main/js/apps/overview/{size => components}/language-distribution.js (87%) create mode 100644 server/sonar-web/src/main/js/apps/overview/components/legend.js rename server/sonar-web/src/main/js/apps/overview/{timeline => components}/timeline-chart.js (100%) delete mode 100644 server/sonar-web/src/main/js/apps/overview/coverage/coverage-measure.js delete mode 100644 server/sonar-web/src/main/js/apps/overview/coverage/main.js delete mode 100644 server/sonar-web/src/main/js/apps/overview/domain/header.js delete mode 100644 server/sonar-web/src/main/js/apps/overview/domain/measures-list.js delete mode 100644 server/sonar-web/src/main/js/apps/overview/domain/timeline.js create mode 100644 server/sonar-web/src/main/js/apps/overview/domains/coverage-domain.js rename server/sonar-web/src/main/js/apps/overview/{issues/main.js => domains/debt-domain.js} (76%) rename server/sonar-web/src/main/js/apps/overview/{duplications/main.js => domains/duplications-domain.js} (69%) rename server/sonar-web/src/main/js/apps/overview/{size/main.js => domains/size-domain.js} (83%) rename server/sonar-web/src/main/js/apps/overview/{main => }/gate/gate-condition.js (78%) rename server/sonar-web/src/main/js/apps/overview/{main => }/gate/gate-conditions.js (100%) rename server/sonar-web/src/main/js/apps/overview/{main => }/gate/gate-empty.js (100%) rename server/sonar-web/src/main/js/apps/overview/{main => }/gate/gate.js (100%) delete mode 100644 server/sonar-web/src/main/js/apps/overview/helpers/donut.js delete mode 100644 server/sonar-web/src/main/js/apps/overview/helpers/measure-variation.js delete mode 100644 server/sonar-web/src/main/js/apps/overview/helpers/measure.js rename server/sonar-web/src/main/js/apps/overview/helpers/{period-label.js => periods.js} (81%) delete mode 100644 server/sonar-web/src/main/js/apps/overview/issues/severities.js rename server/sonar-web/src/main/js/{apps/overview/helpers => components/shared}/drilldown-link.js (76%) rename server/sonar-web/src/main/js/{apps/overview/helpers => components/shared}/issues-link.js (66%) rename server/sonar-web/src/main/js/{apps/overview/helpers/gate-link.js => components/shared/quality-gate-link.js} (76%) rename server/sonar-web/src/main/js/{apps/overview/helpers/profile-link.js => components/shared/quality-profile-link.js} (77%) rename server/sonar-web/src/main/js/{apps/overview/helpers => components/shared}/rating.js (75%) create mode 100644 server/sonar-web/tests/apps/overview/components/complexity-distribution-test.js create mode 100644 server/sonar-web/tests/apps/overview/components/issues-tags-test.js create mode 100644 server/sonar-web/tests/apps/overview/components/language-distribution-test.js create mode 100644 server/sonar-web/tests/apps/overview/components/legend-test.js create mode 100644 server/sonar-web/tests/apps/overview/helpers/metrics-test.js create mode 100644 server/sonar-web/tests/apps/overview/helpers/periods-test.js rename server/sonar-web/tests/apps/{ => overview}/overview-test.js (82%) 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 similarity index 86% rename from server/sonar-web/src/main/js/apps/overview/size/complexity-distribution.js rename to 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
{this.renderBarChart()}
; } -} +}); 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
+ + {formatMeasureVariation(this.props.leak, 'PERCENT')} + +
; + }, + + renderDonut () { + let donutData = [ + { value: this.props.measure, fill: '#85bb43' }, + { value: 100 - this.props.measure, fill: '#d4333f' } + ]; + return
+ +
+ + {formatMeasure(this.props.measure, 'PERCENT')} + +
+
; + }, + + render () { + if (this.props.measure == null) { + return null; + } + + let className = classNames('overview-detailed-measure', { + 'overview-leak': this.props.period + }); + + return
  • +
    + {localizeMetric(this.props.metric)} + {this.renderDonut(this.props.measure)} +
    + {this.renderLeak()} +
  • ; + } +}); 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 ; + }); + return
    + {measures} +
    ; + }, + + 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 ; + }); + return
    + {measures} +
    ; + }, + + 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 ; + }); + return
    {metrics}
    ; + }, + + render () { + return
    + {this.renderOverallCoverage()} + {this.renderOverallNewCoverage()} + + {this.renderUTCoverage()} + {this.renderUTNewCoverage()} + + {this.renderITCoverage()} + {this.renderITNewCoverage()} + + {this.renderListOfMeasures(TEST_METRICS)} + + {this.renderOtherMeasures()} +
    ; + } +}); 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 similarity index 69% rename from server/sonar-web/src/main/js/apps/overview/common-components.js rename to 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({ ; } }); - - -export const Legend = React.createClass({ - render() { - if (!this.props.leakPeriodDate) { - return null; - } - return
    - - -
    ; - } -}); 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 similarity index 97% rename from server/sonar-web/src/main/js/apps/overview/domain/bubble-chart.js rename to 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
    -
    + return
    +

    Project Files

    • X: {this.state.xMetric.name}
    • 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 similarity index 98% rename from server/sonar-web/src/main/js/apps/overview/timeline/domain-timeline.js rename to 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
      -
      + return
      +

      Timeline

      {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 similarity index 96% rename from server/sonar-web/src/main/js/apps/overview/domain/treemap.js rename to 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 ?
    • Color: {this.state.colorMetric.name}
    • : null; - return
      -
      + return
      +

      Treemap

      • Size: {this.state.sizeMetric.name}
      • 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 similarity index 72% rename from server/sonar-web/src/main/js/apps/overview/issues/issue-measure.js rename to 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
        +
          +
        • + Added + + + {formatMeasure(added, getShortType(this.props.type))} + + +
        • +
        • + Removed + + {formatMeasure(removed, getShortType(this.props.type))} + +
        • +
        +
        ; + }, + + render () { + let measure = this.props.measures[this.props.metric]; + if (measure == null) { + return null; + } + + return
        +
        + {localizeMetric(this.props.metric)} + + + {formatMeasure(measure, this.props.type)} + + + {this.props.children} +
        + {this.renderLeak()} +
        ; + } +}); + + export const OnNewCodeMeasure = React.createClass({ renderLeak () { if (!this.props.leakPeriodDate) { @@ -121,11 +175,12 @@ export const OnNewCodeMeasure = React.createClass({
        • On New Code - + {formatMeasure(onNewCode, getShortType(this.props.type))} - +
      ; @@ -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
      • Added + component={this.props.component.key} + params={{ resolved: 'false', severities: this.props.severity, createdAfter: createdAfter }}> {formatMeasure(added, 'SHORT_INT')} @@ -205,9 +263,10 @@ export const SeverityMeasure = React.createClass({ - + {formatMeasure(measure, 'SHORT_INT')} - + {this.props.children}
      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 similarity index 80% rename from server/sonar-web/src/main/js/apps/overview/issues/assignees.js rename to 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 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 similarity index 77% rename from server/sonar-web/src/main/js/apps/overview/issues/tags.js rename to 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 ; } - - 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 similarity index 87% rename from server/sonar-web/src/main/js/apps/overview/size/language-distribution.js rename to 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
      {this.renderBarChart()}
      ; } -} +}); 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
      + + +
      ; + } +}); 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 similarity index 100% rename from server/sonar-web/src/main/js/apps/overview/timeline/timeline-chart.js rename to 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
      - - {formatMeasureVariation(leak, getShortType(this.props.type))} - -
      ; - }, - - renderLeakValue () { - if (!this.props.leakPeriodDate) { - return null; - } - - if (!this.props.leakMetric) { - return
       
      ; - } - - let leak = this.props.leak[this.props.leakMetric]; - - let donutData = [ - { value: leak, fill: '#85bb43' }, - { value: 100 - leak, fill: '#d4333f' } - ]; - - return
      -
      - -
      - - - {formatMeasure(leak, this.props.type)} - - -
      ; - }, - - renderDonut (measure) { - if (this.props.type !== 'PERCENT') { - return null; - } - - let donutData = [ - { value: measure, fill: '#85bb43' }, - { value: 100 - measure, fill: '#d4333f' } - ]; - return
      - -
      ; - }, - - render () { - let measure = this.props.measures[this.props.metric]; - if (measure == null) { - return null; - } - - return
      -
      - {localizeMetric(this.props.metric)} - {this.renderDonut(measure)} - - - {formatMeasure(measure, this.props.type)} - - -
      - {this.renderLeakValue()} - {this.renderLeakVariation()} -
      ; - } -}); 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
      - -
      ; - }, - - renderLegend () { - return ; - }, - - 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
      - - - - - - -
      ; - }, - - renderITCoverage () { - let hasBothTypes = this.state.measures['coverage'] != null && this.state.measures['it_coverage'] != null; - if (!hasBothTypes) { - return null; - } - return
      - - - - - - -
      ; - }, - - renderOverallCoverage () { - return
      - - - - - - -
      ; - }, - - renderListOfMeasures(list) { - let metrics = list - .map(key => _.findWhere(this.props.metrics, { key })) - .map(metric => { - return ; - }); - return
      {metrics}
      ; - }, - - render () { - if (!this.state.ready) { - return this.renderLoading(); - } - let treemapScale = d3.scale.linear() - .domain([0, 100]) - .range(CHART_COLORS_RANGE_PERCENT); - return
      -
      -
      -
      -
      Tests Overview
      - {this.renderLegend()} -
      - {this.renderOverallCoverage()} - {this.renderUTCoverage()} - {this.renderITCoverage()} - {this.renderListOfMeasures(TEST_METRICS)} - {this.renderOtherMeasures()} -
      - -
      - -
      - - -
      -
      ; - - } -}); 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

      {this.props.title}

      ; - } -} 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 - {formatMeasure(value, metricType)} - ; - } else { - return '—'; - } - } - - render () { - let rows = this.props.metricsToDisplay.map(metric => { - let metricObject = this.getMetricObject(metric); - return - {metricObject.name} - - {this.renderValue(this.state.measures[metric], metric, metricObject.type)} - - ; - }); - return - {rows} -
      ; - } -} - -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
      + +
      ; + }, + + renderLegend () { + return ; + }, + + render () { + if (!this.state.ready) { + return this.renderLoading(); + } + + let treemapScale = d3.scale.linear() + .domain([0, 100]) + .range(CHART_COLORS_RANGE_PERCENT); + + return
      +
      +
      +
      +
      Coverage Overview
      + {this.renderLegend()} +
      + +
      + +
      + +
      +
      + +
      +
      + +
      + +
      + +
      +
      +
      ; + + } +}); 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 similarity index 76% rename from server/sonar-web/src/main/js/apps/overview/issues/main.js rename to 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
      -
      -
      -
      +
      +
      +
      Technical Debt Overview
      {this.renderLegend()}
      -
      +
      - SQALE Rating @@ -142,7 +135,7 @@ export const IssuesMain = React.createClass({
      - @@ -159,7 +152,7 @@ export const IssuesMain = React.createClass({
      - +
      @@ -178,21 +171,28 @@ export const IssuesMain = React.createClass({ {this.renderOtherMeasures()}
      - + +
      + +
      -
      - - +
      +
      + +
      +
      + +
      ; 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 similarity index 69% rename from server/sonar-web/src/main/js/apps/overview/duplications/main.js rename to 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
      -
      -
      -
      +
      +
      +
      Duplications Overview
      {this.renderLegend()}
      @@ -95,21 +95,27 @@ export const DuplicationsMain = React.createClass({ {this.renderMeasures()}
      - +
      + +
      -
      - - +
      +
      + +
      +
      + +
      ; 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 similarity index 83% rename from server/sonar-web/src/main/js/apps/overview/size/main.js rename to 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
      -
      -
      +
      +
      Size Overview
      {this.renderLegend()}
      @@ -135,12 +134,16 @@ export const SizeMain = React.createClass({
      -
      - - +
      +
      + +
      +
      + +
      ; 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 similarity index 78% rename from server/sonar-web/src/main/js/apps/overview/main/gate/gate-condition.js rename to 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 {formatted}; + } +}); 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 similarity index 100% rename from server/sonar-web/src/main/js/apps/overview/main/gate/gate-conditions.js rename to 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 similarity index 100% rename from server/sonar-web/src/main/js/apps/overview/main/gate/gate-empty.js rename to 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 similarity index 100% rename from server/sonar-web/src/main/js/apps/overview/main/gate/gate.js rename to 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 ; - } -}); - -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 ; - }); - return - {sectors} - ; - } -}); 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 {formatted}; - } -}); 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 {formatted}; - } -}); 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 similarity index 81% rename from server/sonar-web/src/main/js/apps/overview/helpers/period-label.js rename to 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 - - - - - - {formatMeasure(s.count, 'SHORT_INT')} - - - ; - }); - - return
      - - - {rows} -
      -
      ; - } -} 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
      {this.props.children}
      ; + return
      {this.props.children}
      ; } }); @@ -50,7 +50,7 @@ export const DomainLeakTitle = React.createClass({ export const DomainHeader = React.createClass({ render () { - return
      + return
      {this.props.title}
      ; 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
      + return
      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 (
    • ({profile.language}) - {profile.name} + {profile.name}
    • ); }), @@ -50,7 +50,7 @@ export default React.createClass({
    • {this.props.component.gate.isDefault ? (Default) : null} - {this.props.component.gate.name} + {this.props.component.gate.name}
    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 similarity index 76% rename from server/sonar-web/src/main/js/apps/overview/helpers/drilldown-link.js rename to 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 {this.props.children}; + return {this.props.children}; } }); 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 similarity index 66% rename from server/sonar-web/src/main/js/apps/overview/helpers/issues-link.js rename to 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 {this.props.children}; 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 similarity index 76% rename from server/sonar-web/src/main/js/apps/overview/helpers/gate-link.js rename to 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 {this.props.children}; 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 similarity index 77% rename from server/sonar-web/src/main/js/apps/overview/helpers/profile-link.js rename to 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 {this.props.children}; 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 similarity index 75% rename from server/sonar-web/src/main/js/apps/overview/helpers/rating.js rename to 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; } /* @@ -77,16 +71,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(); + 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(); + 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 = '=17345;java=194342;js=20984'; +const LINES = 1000000; + + +describe('LanguageDistribution', function () { + let props; + + beforeEach(function () { + let renderer = TestUtils.createRenderer(); + renderer.render(); + 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: '' } + ]); + }); + + it('should pass right yTicks', function () { + expect(props.yTicks).to.deep.equal(['java', 'js', '']); + }); + + 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(); + let output = renderer.getRenderOutput(); + expect(output).to.be.null; + }); + + it('should render', function () { + let renderer = TestUtils.createRenderer(); + renderer.render(); + 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 similarity index 82% rename from server/sonar-web/tests/apps/overview-test.js rename to 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 () { + + }); + }); + }); -- 2.39.5