From 07bb9501605ea5c708eb178d19b4770b34a71d60 Mon Sep 17 00:00:00 2001 From: Stas Vilchik Date: Mon, 2 Nov 2015 11:23:36 +0100 Subject: [PATCH] refactor measure formatting --- server/sonar-web/package.json | 4 +- .../apps/issues/facets/creation-date-facet.js | 3 +- .../apps/issues/helpers/format-facet-value.js | 3 +- .../overview/coverage/coverage-details.js | 3 +- .../js/apps/overview/domain/bubble-chart.js | 11 +- .../js/apps/overview/domain/measures-list.js | 3 +- .../main/js/apps/overview/domain/timeline.js | 3 +- .../main/js/apps/overview/domain/treemap.js | 5 +- .../main/js/apps/overview/general/coverage.js | 7 +- .../js/apps/overview/general/duplications.js | 7 +- .../main/js/apps/overview/general/issues.js | 18 +- .../src/main/js/apps/overview/general/size.js | 9 +- .../overview/helpers/measure-variation.js | 4 +- .../main/js/apps/overview/helpers/measure.js | 3 +- .../main/js/apps/overview/helpers/rating.js | 3 +- .../main/js/apps/overview/issues/assignees.js | 4 +- .../js/apps/overview/issues/severities.js | 4 +- .../src/main/js/apps/overview/issues/tags.js | 4 +- .../overview/size/complexity-distribution.js | 3 +- .../overview/size/language-distribution.js | 3 +- .../js/apps/quality-profiles/profile-view.js | 4 +- .../src/main/js/helpers/handlebars-helpers.js | 7 +- .../sonar-web/src/main/js/helpers/measures.js | 248 +++++++++++++++ .../sonar-web/src/main/js/libs/application.js | 300 ------------------ .../main/js/widgets/issue-filter/widget.js | 4 +- server/sonar-web/tests/application-test.js | 165 ---------- .../sonar-web/tests/helpers/measures-test.js | 226 +++++++++++++ 27 files changed, 549 insertions(+), 509 deletions(-) create mode 100644 server/sonar-web/src/main/js/helpers/measures.js create mode 100644 server/sonar-web/tests/helpers/measures-test.js diff --git a/server/sonar-web/package.json b/server/sonar-web/package.json index 9a3c6870090..e73b954a6ac 100644 --- a/server/sonar-web/package.json +++ b/server/sonar-web/package.json @@ -37,6 +37,7 @@ "jsdom": "6.5.1", "mocha": "2.3.3", "moment": "2.10.6", + "numeral": "^1.5.3", "react": "^0.14.0", "react-addons-test-utils": "^0.14.0", "react-dom": "^0.14.0", @@ -58,7 +59,8 @@ "browserify-shim": { "jquery": "global:jQuery", "underscore": "global:_", - "d3": "global:d3" + "d3": "global:d3", + "numeral": "global:numeral" }, "browserify": { "transform": [ diff --git a/server/sonar-web/src/main/js/apps/issues/facets/creation-date-facet.js b/server/sonar-web/src/main/js/apps/issues/facets/creation-date-facet.js index ed5de16a26e..96dfc2500a7 100644 --- a/server/sonar-web/src/main/js/apps/issues/facets/creation-date-facet.js +++ b/server/sonar-web/src/main/js/apps/issues/facets/creation-date-facet.js @@ -4,6 +4,7 @@ import moment from 'moment'; import BaseFacet from './base-facet'; import Template from '../templates/facets/issues-creation-date-facet.hbs'; import '../../../components/widgets/barchart.js'; +import { formatMeasure } from '../../../helpers/measures'; export default BaseFacet.extend({ template: Template, @@ -49,7 +50,7 @@ export default BaseFacet.extend({ } values = values.map(function (v) { var format = that.options.app.state.getFacetMode() === 'count' ? 'SHORT_INT' : 'SHORT_WORK_DUR'; - var text = window.formatMeasure(v.count, format); + var text = formatMeasure(v.count, format); return _.extend(v, { text: text }); }); return this.$('.js-barchart').barchart(values); diff --git a/server/sonar-web/src/main/js/apps/issues/helpers/format-facet-value.js b/server/sonar-web/src/main/js/apps/issues/helpers/format-facet-value.js index 94ddec317a4..5d9ba4250ae 100644 --- a/server/sonar-web/src/main/js/apps/issues/helpers/format-facet-value.js +++ b/server/sonar-web/src/main/js/apps/issues/helpers/format-facet-value.js @@ -1,8 +1,9 @@ import Handlebars from 'hbsfy/runtime'; +import { formatMeasure } from '../../../helpers/measures'; Handlebars.registerHelper('formatFacetValue', function (value, facetMode) { var formatter = facetMode === 'debt' ? 'SHORT_WORK_DUR' : 'SHORT_INT'; - return window.formatMeasure(value, formatter); + return formatMeasure(value, formatter); }); diff --git a/server/sonar-web/src/main/js/apps/overview/coverage/coverage-details.js b/server/sonar-web/src/main/js/apps/overview/coverage/coverage-details.js index 64904641c15..3b0057038d0 100644 --- a/server/sonar-web/src/main/js/apps/overview/coverage/coverage-details.js +++ b/server/sonar-web/src/main/js/apps/overview/coverage/coverage-details.js @@ -2,6 +2,7 @@ import React from 'react'; import { getMeasures } from '../../../api/measures'; import DrilldownLink from '../helpers/drilldown-link'; +import { formatMeasure } from '../../../helpers/measures'; const METRICS = [ @@ -36,7 +37,7 @@ export class CoverageDetails extends React.Component { renderValue (value, metricKey) { if (value != null) { return - {window.formatMeasure(value, 'PERCENT')} + {formatMeasure(value, 'PERCENT')} ; } else { 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/domain/bubble-chart.js index 6adef652fe6..4a72a92dffc 100644 --- a/server/sonar-web/src/main/js/apps/overview/domain/bubble-chart.js +++ b/server/sonar-web/src/main/js/apps/overview/domain/bubble-chart.js @@ -3,6 +3,7 @@ import React from 'react'; import { BubbleChart } from '../../../components/charts/bubble-chart'; import { getProjectUrl } from '../../../helpers/Url'; import { getFiles } from '../../../api/components'; +import { formatMeasure } from '../../../helpers/measures'; const HEIGHT = 360; @@ -63,9 +64,9 @@ export class DomainBubbleChart extends React.Component { let inner = [ component.name, - `${this.state.xMetric.name}: ${window.formatMeasure(getMeasure(component, this.props.xMetric), this.state.xMetric.type)}`, - `${this.state.yMetric.name}: ${window.formatMeasure(getMeasure(component, this.props.yMetric), this.state.yMetric.type)}`, - `${sizeMetricsTitle}: ${window.formatMeasure(this.getSizeMetricsValue(component), sizeMetricsType)}` + `${this.state.xMetric.name}: ${formatMeasure(getMeasure(component, this.props.xMetric), this.state.xMetric.type)}`, + `${this.state.yMetric.name}: ${formatMeasure(getMeasure(component, this.props.yMetric), this.state.yMetric.type)}`, + `${sizeMetricsTitle}: ${formatMeasure(this.getSizeMetricsValue(component), sizeMetricsType)}` ].join('
'); return `
${inner}
`; } @@ -90,8 +91,8 @@ export class DomainBubbleChart extends React.Component { tooltip: this.getTooltip(component) }; }); - let formatXTick = (tick) => window.formatMeasure(tick, this.state.xMetric.type); - let formatYTick = (tick) => window.formatMeasure(tick, this.state.yMetric.type); + let formatXTick = (tick) => formatMeasure(tick, this.state.xMetric.type); + let formatYTick = (tick) => formatMeasure(tick, this.state.yMetric.type); return - {window.formatMeasure(value, metricType)} + {formatMeasure(value, metricType)} ; } else { return '—'; 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 index cef1012c012..88b95bcef34 100644 --- a/server/sonar-web/src/main/js/apps/overview/domain/timeline.js +++ b/server/sonar-web/src/main/js/apps/overview/domain/timeline.js @@ -5,6 +5,7 @@ import React from 'react'; import { LineChart } from '../../../components/charts/line-chart'; import { getTimeMachineData } from '../../../api/time-machine'; import { getEvents } from '../../../api/events'; +import { formatMeasure } from '../../../helpers/measures'; const HEIGHT = 280; @@ -112,7 +113,7 @@ export class DomainTimeline extends React.Component { let xTicks = events.map(event => event.version.substr(0, 6)); let xValues = events.map(event => { - return currentMetricType === 'RATING' ? event.value : window.formatMeasure(event.value, currentMetricType); + return currentMetricType === 'RATING' ? event.value : formatMeasure(event.value, currentMetricType); }); // TODO use leak period diff --git a/server/sonar-web/src/main/js/apps/overview/domain/treemap.js b/server/sonar-web/src/main/js/apps/overview/domain/treemap.js index 1592b58a8e2..2bc32e36b7e 100644 --- a/server/sonar-web/src/main/js/apps/overview/domain/treemap.js +++ b/server/sonar-web/src/main/js/apps/overview/domain/treemap.js @@ -3,6 +3,7 @@ import React from 'react'; import { Treemap } from '../../../components/charts/treemap'; import { getChildren } from '../../../api/components'; +import { formatMeasure } from '../../../helpers/measures'; const HEIGHT = 360; @@ -43,10 +44,10 @@ export class DomainTreemap extends React.Component { getTooltip (component) { let inner = [ component.name, - `${this.state.sizeMetric.name}: ${window.formatMeasure(component.measures[this.props.sizeMetric], this.state.sizeMetric.type)}` + `${this.state.sizeMetric.name}: ${formatMeasure(component.measures[this.props.sizeMetric], this.state.sizeMetric.type)}` ]; if (this.state.colorMetric) { - inner.push(`${this.state.colorMetric.name}: ${window.formatMeasure(component.measures[this.props.colorMetric], this.state.colorMetric.type)}`); + inner.push(`${this.state.colorMetric.name}: ${formatMeasure(component.measures[this.props.colorMetric], this.state.colorMetric.type)}`); } inner = inner.join('
'); return `
${inner}
`; diff --git a/server/sonar-web/src/main/js/apps/overview/general/coverage.js b/server/sonar-web/src/main/js/apps/overview/general/coverage.js index 5637d4c227d..2af648184ac 100644 --- a/server/sonar-web/src/main/js/apps/overview/general/coverage.js +++ b/server/sonar-web/src/main/js/apps/overview/general/coverage.js @@ -4,6 +4,7 @@ import { Domain, DomainHeader, DomainPanel, DomainNutshell, DomainLeak, Measures import DrilldownLink from '../helpers/drilldown-link'; import { TooltipsMixin } from '../../../components/mixins/tooltips-mixin'; import { getMetricName } from '../helpers/metrics'; +import { formatMeasure } from '../../../helpers/measures'; export const GeneralCoverage = React.createClass({ @@ -18,7 +19,7 @@ export const GeneralCoverage = React.createClass({ renderNewCoverage () { if (this.props.leak['new_overall_coverage'] != null) { return - {window.formatMeasure(this.props.leak['new_overall_coverage'], 'PERCENT')} + {formatMeasure(this.props.leak['new_overall_coverage'], 'PERCENT')} ; } else { return —; @@ -52,12 +53,12 @@ export const GeneralCoverage = React.createClass({ - {window.formatMeasure(this.props.measures['overall_coverage'], 'PERCENT')} + {formatMeasure(this.props.measures['overall_coverage'], 'PERCENT')} - {window.formatMeasure(this.props.measures['tests'], 'SHORT_INT')} + {formatMeasure(this.props.measures['tests'], 'SHORT_INT')} diff --git a/server/sonar-web/src/main/js/apps/overview/general/duplications.js b/server/sonar-web/src/main/js/apps/overview/general/duplications.js index d316eea8ba6..bd95a2d4a39 100644 --- a/server/sonar-web/src/main/js/apps/overview/general/duplications.js +++ b/server/sonar-web/src/main/js/apps/overview/general/duplications.js @@ -4,6 +4,7 @@ import { Domain, DomainHeader, DomainPanel, DomainNutshell, DomainLeak, Measures import DrilldownLink from '../helpers/drilldown-link'; import { TooltipsMixin } from '../../../components/mixins/tooltips-mixin'; import { getMetricName } from '../helpers/metrics'; +import { formatMeasure, formatMeasureVariation } from '../../../helpers/measures'; export const GeneralDuplications = React.createClass({ @@ -21,7 +22,7 @@ export const GeneralDuplications = React.createClass({ return - {window.formatMeasureVariation(this.props.leak['duplicated_lines_density'], 'PERCENT')} + {formatMeasureVariation(this.props.leak['duplicated_lines_density'], 'PERCENT')} {this.renderTimeline('after')} @@ -38,12 +39,12 @@ export const GeneralDuplications = React.createClass({ - {window.formatMeasure(this.props.measures['duplicated_lines_density'], 'PERCENT')} + {formatMeasure(this.props.measures['duplicated_lines_density'], 'PERCENT')} - {window.formatMeasure(this.props.measures['duplicated_blocks'], 'SHORT_INT')} + {formatMeasure(this.props.measures['duplicated_blocks'], 'SHORT_INT')} diff --git a/server/sonar-web/src/main/js/apps/overview/general/issues.js b/server/sonar-web/src/main/js/apps/overview/general/issues.js index b97b0db57f9..e34bb88b073 100644 --- a/server/sonar-web/src/main/js/apps/overview/general/issues.js +++ b/server/sonar-web/src/main/js/apps/overview/general/issues.js @@ -11,6 +11,7 @@ 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'; export const GeneralIssues = React.createClass({ @@ -30,7 +31,7 @@ export const GeneralIssues = React.createClass({ - {window.formatMeasure(measure, 'SHORT_INT')} + {formatMeasure(measure, 'SHORT_INT')} ; @@ -55,13 +56,13 @@ export const GeneralIssues = React.createClass({ - {window.formatMeasureVariation(this.props.leak.issues, 'SHORT_INT')} + {formatMeasureVariation(this.props.leak.issues, 'SHORT_INT')} - {window.formatMeasureVariation(this.props.leak.debt, 'SHORT_WORK_DUR')} + {formatMeasureVariation(this.props.leak.debt, 'SHORT_WORK_DUR')} @@ -70,22 +71,21 @@ export const GeneralIssues = React.createClass({ - {window.formatMeasureVariation(this.props.leak.issuesSeverities[0], 'SHORT_INT')} + {formatMeasureVariation(this.props.leak.issuesSeverities[0], 'SHORT_INT')} - {window.formatMeasureVariation(this.props.leak.issuesSeverities[1], 'SHORT_INT')} + {formatMeasureVariation(this.props.leak.issuesSeverities[1], 'SHORT_INT')} - {window.formatMeasureVariation(this.props.leak.issuesStatuses[0] + this.props.leak.issuesStatuses[1], - 'SHORT_INT')} + {formatMeasureVariation(this.props.leak.issuesStatuses[0] + this.props.leak.issuesStatuses[1], 'SHORT_INT')} @@ -108,12 +108,12 @@ export const GeneralIssues = React.createClass({ - {window.formatMeasure(this.props.measures.issues, 'SHORT_INT')} + {formatMeasure(this.props.measures.issues, 'SHORT_INT')} - {window.formatMeasure(this.props.measures.debt, 'SHORT_WORK_DUR')} + {formatMeasure(this.props.measures.debt, 'SHORT_WORK_DUR')} diff --git a/server/sonar-web/src/main/js/apps/overview/general/size.js b/server/sonar-web/src/main/js/apps/overview/general/size.js index ac43ab6b001..8642b9918ae 100644 --- a/server/sonar-web/src/main/js/apps/overview/general/size.js +++ b/server/sonar-web/src/main/js/apps/overview/general/size.js @@ -4,6 +4,7 @@ import { Domain, DomainHeader, DomainPanel, DomainNutshell, DomainLeak, Measures import DrilldownLink from '../helpers/drilldown-link'; import { TooltipsMixin } from '../../../components/mixins/tooltips-mixin'; import { getMetricName } from '../helpers/metrics'; +import { formatMeasure, formatMeasureVariation } from '../../../helpers/measures'; export const GeneralSize = React.createClass({ @@ -22,10 +23,10 @@ export const GeneralSize = React.createClass({ return - {window.formatMeasureVariation(this.props.leak['ncloc'], 'SHORT_INT')} + {formatMeasureVariation(this.props.leak['ncloc'], 'SHORT_INT')} - {window.formatMeasureVariation(this.props.leak['files'], 'SHORT_INT')} + {formatMeasureVariation(this.props.leak['files'], 'SHORT_INT')} {this.renderTimeline('after')} @@ -42,12 +43,12 @@ export const GeneralSize = React.createClass({ - {window.formatMeasure(this.props.measures['ncloc'], 'SHORT_INT')} + {formatMeasure(this.props.measures['ncloc'], 'SHORT_INT')} - {window.formatMeasure(this.props.measures['files'], 'SHORT_INT')} + {formatMeasure(this.props.measures['files'], 'SHORT_INT')} 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 index 0e2e1fa75c4..e6ea091ce87 100644 --- 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 @@ -1,11 +1,13 @@ 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 = window.formatMeasureVariation(this.props.value, this.props.type); + 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 index dafc98a82c4..3d4eb5ec549 100644 --- a/server/sonar-web/src/main/js/apps/overview/helpers/measure.js +++ b/server/sonar-web/src/main/js/apps/overview/helpers/measure.js @@ -1,11 +1,12 @@ 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 = window.formatMeasure(this.props.value, this.props.type); + let formatted = formatMeasure(this.props.value, this.props.type); return {formatted}; } }); diff --git a/server/sonar-web/src/main/js/apps/overview/helpers/rating.js b/server/sonar-web/src/main/js/apps/overview/helpers/rating.js index e160019e9c6..5568dc5d2dc 100644 --- a/server/sonar-web/src/main/js/apps/overview/helpers/rating.js +++ b/server/sonar-web/src/main/js/apps/overview/helpers/rating.js @@ -1,11 +1,12 @@ 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 = window.formatMeasure(this.props.value, 'RATING'); + let formatted = formatMeasure(this.props.value, 'RATING'); let className = 'rating rating-' + formatted; return {formatted}; } diff --git a/server/sonar-web/src/main/js/apps/overview/issues/assignees.js b/server/sonar-web/src/main/js/apps/overview/issues/assignees.js index 2d0905c8030..87c55e1de69 100644 --- a/server/sonar-web/src/main/js/apps/overview/issues/assignees.js +++ b/server/sonar-web/src/main/js/apps/overview/issues/assignees.js @@ -2,6 +2,8 @@ import React from 'react'; import Assignee from '../../../components/shared/assignee-helper'; import { DomainHeader } from '../domain/header'; import { componentIssuesUrl } from '../../../helpers/Url'; +import { formatMeasure } from '../../../helpers/measures'; + export default class extends React.Component { render () { @@ -12,7 +14,7 @@ export default class extends React.Component { - {window.formatMeasure(s.count, 'SHORT_INT')} + {formatMeasure(s.count, 'SHORT_INT')} ; }); 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 index 5ccb3098b86..efd0e8e3184 100644 --- a/server/sonar-web/src/main/js/apps/overview/issues/severities.js +++ b/server/sonar-web/src/main/js/apps/overview/issues/severities.js @@ -3,6 +3,8 @@ import React from 'react'; import SeverityHelper from '../../../components/shared/severity-helper'; import { DomainHeader } from '../domain/header'; import { componentIssuesUrl } from '../../../helpers/Url'; +import { formatMeasure } from '../../../helpers/measures'; + export default class extends React.Component { sortedSeverities () { @@ -18,7 +20,7 @@ export default class extends React.Component { - {window.formatMeasure(s.count, 'SHORT_INT')} + {formatMeasure(s.count, 'SHORT_INT')} ; diff --git a/server/sonar-web/src/main/js/apps/overview/issues/tags.js b/server/sonar-web/src/main/js/apps/overview/issues/tags.js index 8c29d00f683..4d86824d25f 100644 --- a/server/sonar-web/src/main/js/apps/overview/issues/tags.js +++ b/server/sonar-web/src/main/js/apps/overview/issues/tags.js @@ -2,12 +2,14 @@ import React from 'react'; import { DomainHeader } from '../domain/header'; import { WordCloud } from '../../../components/charts/word-cloud'; import { componentIssuesUrl } from '../../../helpers/Url'; +import { formatMeasure } from '../../../helpers/measures'; + export default class extends React.Component { renderWordCloud () { let tags = this.props.tags.map(tag => { let link = componentIssuesUrl(this.props.component.key, { resolved: 'false', tags: tag.val }); - let tooltip = `Issues: ${window.formatMeasure(tag.count, 'SHORT_INT')}`; + let tooltip = `Issues: ${formatMeasure(tag.count, 'SHORT_INT')}`; return { text: tag.val, size: tag.count, link, tooltip }; }); return ; diff --git a/server/sonar-web/src/main/js/apps/overview/size/complexity-distribution.js b/server/sonar-web/src/main/js/apps/overview/size/complexity-distribution.js index ac5796fa584..9582e985f3c 100644 --- a/server/sonar-web/src/main/js/apps/overview/size/complexity-distribution.js +++ b/server/sonar-web/src/main/js/apps/overview/size/complexity-distribution.js @@ -2,6 +2,7 @@ import React from 'react'; import { BarChart } from '../../../components/charts/bar-chart'; import { getMeasures } from '../../../api/measures'; +import { formatMeasure } from '../../../helpers/measures'; const HEIGHT = 120; @@ -42,7 +43,7 @@ export class ComplexityDistribution extends React.Component { let xTicks = data.map(point => point.value); - let xValues = data.map(point => window.formatMeasure(point.y, 'INT')); + let xValues = data.map(point => formatMeasure(point.y, 'INT')); return this.getLanguageName(d.lang)); - let xValues = data.map(d => window.formatMeasure(d.y, 'INT')); + let xValues = data.map(d => formatMeasure(d.y, 'INT')); return = 1000) { + format = '0.[0]a'; + } + if (value >= 10000) { + format = '0a'; + } + return numeral(value).format(format); +} + +function shortIntVariationFormatter (value) { + let formatted = shortIntFormatter(Math.abs(value)); + return value < 0 ? `-${formatted}` : `+${formatted}`; +} + +function floatFormatter (value) { + return numeral(value).format('0,0.0'); +} + +function floatVariationFormatter (value) { + return value === 0 ? '+0.0' : numeral(value).format('+0,0.0'); +} + +function percentFormatter (value) { + value = parseFloat(value); + return numeral(value / 100).format('0,0.0%'); +} + +function percentVariationFormatter (value) { + value = parseFloat(value); + return value === 0 ? '+0.0%' : numeral(value / 100).format('+0,0.0%'); +} + +function ratingFormatter (value) { + value = parseInt(value, 10); + return String.fromCharCode(97 + value - 1).toUpperCase(); +} + +function levelFormatter (value) { + var l10nKey = 'metric.level.' + value, + result = window.t(l10nKey); + // if couldn't translate, return the initial value + return l10nKey !== result ? result : value; +} + +function millisecondsFormatter (value) { + const ONE_SECOND = 1000; + const ONE_MINUTE = 60 * ONE_SECOND; + if (value >= ONE_MINUTE) { + let minutes = Math.round(value / ONE_MINUTE); + return `${minutes}min`; + } else if (value >= ONE_SECOND) { + let seconds = Math.round(value / ONE_SECOND); + return `${seconds}s`; + } else { + return `${value}ms`; + } +} + + +/* + * Debt Formatters + */ + +function shouldDisplayDays (days) { + return days > 0; +} + +function shouldDisplayHours (days, hours) { + return hours > 0 && days < 10; +} + +function shouldDisplayHoursInShortFormat (days, hours) { + return hours > 0 && days === 0; +} + +function shouldDisplayMinutes (days, hours, minutes) { + return minutes > 0 && hours < 10 && days === 0; +} + +function shouldDisplayMinutesInShortFormat (days, hours, minutes) { + return minutes > 0 && hours === 0 && days === 0; +} + +function addSpaceIfNeeded (value) { + return value.length > 0 ? value + ' ' : value; +} + +function formatDuration (isNegative, days, hours, minutes) { + var formatted = ''; + if (shouldDisplayDays(days)) { + formatted += window.tp('work_duration.x_days', isNegative ? -1 * days : days); + } + if (shouldDisplayHours(days, hours)) { + formatted = addSpaceIfNeeded(formatted); + formatted += window.tp('work_duration.x_hours', isNegative && formatted.length === 0 ? -1 * hours : hours); + } + if (shouldDisplayMinutes(days, hours, minutes)) { + formatted = addSpaceIfNeeded(formatted); + formatted += window.tp('work_duration.x_minutes', isNegative && formatted.length === 0 ? -1 * minutes : minutes); + } + return formatted; +} + +function formatDurationShort (isNegative, days, hours, minutes) { + var formatted = ''; + if (shouldDisplayDays(days)) { + var formattedDays = formatMeasure(isNegative ? -1 * days : days, 'SHORT_INT'); + formatted += window.tp('work_duration.x_days', formattedDays); + } + if (shouldDisplayHoursInShortFormat(days, hours)) { + formatted = addSpaceIfNeeded(formatted); + formatted += window.tp('work_duration.x_hours', isNegative && formatted.length === 0 ? -1 * hours : hours); + } + if (shouldDisplayMinutesInShortFormat(days, hours, minutes)) { + formatted = addSpaceIfNeeded(formatted); + formatted += window.tp('work_duration.x_minutes', isNegative && formatted.length === 0 ? -1 * minutes : minutes); + } + return formatted; +} + +function durationFormatter (value) { + if (value === 0) { + return '0'; + } + var hoursInDay = window.SS.hoursInDay, + isNegative = value < 0, + absValue = Math.abs(value); + var days = Math.round(absValue / hoursInDay / 60); + var remainingValue = absValue - days * hoursInDay * 60; + var hours = Math.round(remainingValue / 60); + remainingValue -= hours * 60; + return formatDuration(isNegative, days, hours, remainingValue); +} + +function shortDurationFormatter (value) { + value = parseInt(value, 10); + if (value === 0) { + return '0'; + } + var hoursInDay = window.SS.hoursInDay, + isNegative = value < 0, + absValue = Math.abs(value); + var days = Math.floor(absValue / hoursInDay / 60); + var remainingValue = absValue - days * hoursInDay * 60; + var hours = Math.floor(remainingValue / 60); + remainingValue -= hours * 60; + return formatDurationShort(isNegative, days, hours, remainingValue); +} + +function durationVariationFormatter (value) { + if (value === 0) { + return '0'; + } + var formatted = durationFormatter(value); + return formatted[0] !== '-' ? '+' + formatted : formatted; +} + +function shortDurationVariationFormatter (value) { + if (value === 0) { + return '+0'; + } + var formatted = shortDurationFormatter(value); + return formatted[0] !== '-' ? '+' + formatted : formatted; +} diff --git a/server/sonar-web/src/main/js/libs/application.js b/server/sonar-web/src/main/js/libs/application.js index 9613272304c..eb55b2a7434 100644 --- a/server/sonar-web/src/main/js/libs/application.js +++ b/server/sonar-web/src/main/js/libs/application.js @@ -262,306 +262,6 @@ function closeModalWindow () { -/* - * Measures - */ - -(function () { - - function shortIntFormatter (value) { - var format = '0,0'; - if (value >= 1000) { - format = '0.[0]a'; - } - if (value >= 10000) { - format = '0a'; - } - return numeral(value).format(format); - } - - function shortIntVariationFormatter (value) { - if (value === 0) { - return '+0'; - } - var format = '+0,0'; - if (Math.abs(value) >= 1000) { - format = '+0.[0]a'; - } - if (Math.abs(value) >= 10000) { - format = '+0a'; - } - return numeral(value).format(format); - } - - /** - * Check if days should be displayed for a work duration - * @param {number} days - * @returns {boolean} - */ - function shouldDisplayDays (days) { - return days > 0; - } - - /** - * Check if hours should be displayed for a work duration - * @param {number} days - * @param {number} hours - * @returns {boolean} - */ - function shouldDisplayHours (days, hours) { - return hours > 0 && days < 10; - } - - /** - * Check if hours should be displayed for a work duration - * @param {number} days - * @param {number} hours - * @returns {boolean} - */ - function shouldDisplayHoursInShortFormat (days, hours) { - return hours > 0 && days === 0; - } - - /** - * Check if minutes should be displayed for a work duration - * @param {number} days - * @param {number} hours - * @param {number} minutes - * @returns {boolean} - */ - function shouldDisplayMinutes (days, hours, minutes) { - return minutes > 0 && hours < 10 && days === 0; - } - - /** - * Check if minutes should be displayed for a work duration - * @param {number} days - * @param {number} hours - * @param {number} minutes - * @returns {boolean} - */ - function shouldDisplayMinutesInShortFormat (days, hours, minutes) { - return minutes > 0 && hours === 0 && days === 0; - } - - /** - * Add a space between units if needed - * @param {string} value - * @returns {string} - */ - function addSpaceIfNeeded (value) { - return value.length > 0 ? value + ' ' : value; - } - - /** - * Format a work duration based on parameters - * @param {bool} isNegative - * @param {number} days - * @param {number} hours - * @param {number} minutes - * @returns {string} - */ - function formatDuration (isNegative, days, hours, minutes) { - var formatted = ''; - if (shouldDisplayDays(days)) { - formatted += tp('work_duration.x_days', isNegative ? -1 * days : days); - } - if (shouldDisplayHours(days, hours)) { - formatted = addSpaceIfNeeded(formatted); - formatted += tp('work_duration.x_hours', isNegative && formatted.length === 0 ? -1 * hours : hours); - } - if (shouldDisplayMinutes(days, hours, minutes)) { - formatted = addSpaceIfNeeded(formatted); - formatted += tp('work_duration.x_minutes', isNegative && formatted.length === 0 ? -1 * minutes : minutes); - } - return formatted; - } - - /** - * Format a work duration based on parameters - * @param {bool} isNegative - * @param {number} days - * @param {number} hours - * @param {number} minutes - * @returns {string} - */ - function formatDurationShort (isNegative, days, hours, minutes) { - var formatted = ''; - if (shouldDisplayDays(days)) { - var formattedDays = window.formatMeasure(isNegative ? -1 * days : days, 'SHORT_INT'); - formatted += tp('work_duration.x_days', formattedDays); - } - if (shouldDisplayHoursInShortFormat(days, hours)) { - formatted = addSpaceIfNeeded(formatted); - formatted += tp('work_duration.x_hours', isNegative && formatted.length === 0 ? -1 * hours : hours); - } - if (shouldDisplayMinutesInShortFormat(days, hours, minutes)) { - formatted = addSpaceIfNeeded(formatted); - formatted += tp('work_duration.x_minutes', isNegative && formatted.length === 0 ? -1 * minutes : minutes); - } - return formatted; - } - - /** - * Format a work duration measure - * @param {number} value - * @returns {string} - */ - var durationFormatter = function (value) { - if (value === 0) { - return '0'; - } - var hoursInDay = window.SS.hoursInDay || 8, - isNegative = value < 0, - absValue = Math.abs(value); - var days = Math.round(absValue / hoursInDay / 60); - var remainingValue = absValue - days * hoursInDay * 60; - var hours = Math.round(remainingValue / 60); - remainingValue -= hours * 60; - return formatDuration(isNegative, days, hours, remainingValue); - }; - - /** - * Format a work duration measure - * @param {number} value - * @returns {string} - */ - var shortDurationFormatter = function (value) { - value = parseInt(value, 10); - if (value === 0) { - return '0'; - } - var hoursInDay = window.SS.hoursInDay || 8, - isNegative = value < 0, - absValue = Math.abs(value); - var days = Math.floor(absValue / hoursInDay / 60); - var remainingValue = absValue - days * hoursInDay * 60; - var hours = Math.floor(remainingValue / 60); - remainingValue -= hours * 60; - return formatDurationShort(isNegative, days, hours, remainingValue); - }; - - /** - * Format a work duration variation - * @param {number} value - * @returns {string} - */ - var durationVariationFormatter = function (value) { - if (value === 0) { - return '0'; - } - var formatted = durationFormatter(value); - return formatted[0] !== '-' ? '+' + formatted : formatted; - }; - - /** - * Format a work duration variation - * @param {number} value - * @returns {string} - */ - var shortDurationVariationFormatter = function (value) { - if (value === 0) { - return '+0'; - } - var formatted = shortDurationFormatter(value); - return formatted[0] !== '-' ? '+' + formatted : formatted; - }; - - /** - * Format a rating measure - * @param {number} value - */ - var ratingFormatter = function (value) { - value = parseInt(value, 10); - return String.fromCharCode(97 + value - 1).toUpperCase(); - }; - - - /** - * Format a level measure - * @param {number} value - */ - var levelFormatter = function (value) { - var l10nKey = 'metric.level.' + value, - result = window.t(l10nKey); - // if couldn't translate, return the initial value - return l10nKey !== result ? result : value; - }; - - - /** - * Format a milliseconds measure - * @param {number} value - */ - var millisecondsFormatter = function (value) { - return value + ' ms'; - }; - - /** - * Format a measure according to its type - * @param measure - * @param {string} type - * @returns {string|null} - */ - window.formatMeasure = function (measure, type) { - var formatted = null, - formatters = { - 'INT': function (value) { - return numeral(value).format('0,0'); - }, - 'SHORT_INT': shortIntFormatter, - 'FLOAT': function (value) { - return numeral(value).format('0,0.0'); - }, - 'PERCENT': function (value) { - return numeral(+value / 100).format('0,0.0%'); - }, - 'SHORT_PERCENT': function (value) { - return numeral(+value / 100).format('0,0%'); - }, - 'WORK_DUR': durationFormatter, - 'SHORT_WORK_DUR': shortDurationFormatter, - 'RATING': ratingFormatter, - 'LEVEL': levelFormatter, - 'MILLISEC': millisecondsFormatter - }; - if (measure != null && type != null) { - formatted = formatters[type] != null ? formatters[type](measure) : measure; - } - return formatted; - }; - - /** - * Format a measure variation according to its type - * @param measure - * @param {string} type - * @returns {string|null} - */ - window.formatMeasureVariation = function (measure, type) { - var formatted = null, - formatters = { - 'INT': function (value) { - return value === 0 ? '0' : numeral(value).format('+0,0'); - }, - 'SHORT_INT': shortIntVariationFormatter, - 'FLOAT': function (value) { - return value === 0 ? '+0.0' : numeral(value).format('+0,0.0'); - }, - 'PERCENT': function (value) { - return value === 0 ? '+0.0%' : numeral(+value / 100).format('+0,0.0%'); - }, - 'WORK_DUR': durationVariationFormatter, - 'SHORT_WORK_DUR': shortDurationVariationFormatter - }; - if (measure != null && type != null) { - formatted = formatters[type] != null ? formatters[type](measure) : measure; - } - return formatted; - }; -})(); - - - /* * Users */ diff --git a/server/sonar-web/src/main/js/widgets/issue-filter/widget.js b/server/sonar-web/src/main/js/widgets/issue-filter/widget.js index 9fee24ad969..32782666a51 100644 --- a/server/sonar-web/src/main/js/widgets/issue-filter/widget.js +++ b/server/sonar-web/src/main/js/widgets/issue-filter/widget.js @@ -15,6 +15,8 @@ import StatusesTemplate from './templates/widget-issue-filter-statuses.hbs'; import LimitPartial from './templates/_widget-issue-filter-limit.hbs'; import TotalPartial from './templates/_widget-issue-filter-total.hbs'; +import { formatMeasure } from '../../helpers/measures'; + import '../../helpers/handlebars-helpers'; var FACET_LIMIT = 15, @@ -228,7 +230,7 @@ Handlebars.registerHelper('issueFilterTotalLink', function (query, mode) { Handlebars.registerHelper('issueFilterValue', function (value, mode) { var formatter = mode === 'debt' ? 'SHORT_WORK_DUR' : 'SHORT_INT'; - return window.formatMeasure(value, formatter); + return formatMeasure(value, formatter); }); Handlebars.registerPartial('_widget-issue-filter-limit', LimitPartial); diff --git a/server/sonar-web/tests/application-test.js b/server/sonar-web/tests/application-test.js index e7705ee3eeb..14146be362f 100644 --- a/server/sonar-web/tests/application-test.js +++ b/server/sonar-web/tests/application-test.js @@ -45,171 +45,6 @@ describe.skip('Application', function () { }); }); - describe('Measures', function () { - var HOURS_IN_DAY = 8, - ONE_MINUTE = 1, - ONE_HOUR = ONE_MINUTE * 60, - ONE_DAY = HOURS_IN_DAY * ONE_HOUR; - - before(function () { - window.messages = { - 'work_duration.x_days': '{0}d', - 'work_duration.x_hours': '{0}h', - 'work_duration.x_minutes': '{0}min', - 'work_duration.about': '~ {0}' - }; - window.SS = { hoursInDay: HOURS_IN_DAY }; - }); - - describe('#formatMeasure()', function () { - it('should format INT', function () { - assert.equal(window.formatMeasure(0, 'INT'), '0'); - assert.equal(window.formatMeasure(1, 'INT'), '1'); - assert.equal(window.formatMeasure(-5, 'INT'), '-5'); - assert.equal(window.formatMeasure(999, 'INT'), '999'); - assert.equal(window.formatMeasure(1000, 'INT'), '1,000'); - assert.equal(window.formatMeasure(1529, 'INT'), '1,529'); - assert.equal(window.formatMeasure(10000, 'INT'), '10,000'); - assert.equal(window.formatMeasure(1234567890, 'INT'), '1,234,567,890'); - }); - - it('should format SHORT_INT', function () { - assert.equal(window.formatMeasure(0, 'SHORT_INT'), '0'); - assert.equal(window.formatMeasure(1, 'SHORT_INT'), '1'); - assert.equal(window.formatMeasure(999, 'SHORT_INT'), '999'); - assert.equal(window.formatMeasure(1000, 'SHORT_INT'), '1k'); - assert.equal(window.formatMeasure(1529, 'SHORT_INT'), '1.5k'); - assert.equal(window.formatMeasure(10000, 'SHORT_INT'), '10k'); - assert.equal(window.formatMeasure(10678, 'SHORT_INT'), '11k'); - assert.equal(window.formatMeasure(1234567890, 'SHORT_INT'), '1b'); - }); - - it('should format FLOAT', function () { - assert.equal(window.formatMeasure(0.0, 'FLOAT'), '0.0'); - assert.equal(window.formatMeasure(1.0, 'FLOAT'), '1.0'); - assert.equal(window.formatMeasure(1.3, 'FLOAT'), '1.3'); - assert.equal(window.formatMeasure(1.34, 'FLOAT'), '1.3'); - assert.equal(window.formatMeasure(50.89, 'FLOAT'), '50.9'); - assert.equal(window.formatMeasure(100.0, 'FLOAT'), '100.0'); - assert.equal(window.formatMeasure(123.456, 'FLOAT'), '123.5'); - assert.equal(window.formatMeasure(123456.7, 'FLOAT'), '123,456.7'); - assert.equal(window.formatMeasure(1234567890.0, 'FLOAT'), '1,234,567,890.0'); - }); - - it('should format PERCENT', function () { - assert.equal(window.formatMeasure(0.0, 'PERCENT'), '0.0%'); - assert.equal(window.formatMeasure(1.0, 'PERCENT'), '1.0%'); - assert.equal(window.formatMeasure(1.3, 'PERCENT'), '1.3%'); - assert.equal(window.formatMeasure(1.34, 'PERCENT'), '1.3%'); - assert.equal(window.formatMeasure(50.89, 'PERCENT'), '50.9%'); - assert.equal(window.formatMeasure(100.0, 'PERCENT'), '100.0%'); - }); - - it('should format WORK_DUR', function () { - assert.equal(window.formatMeasure(0, 'WORK_DUR'), '0'); - assert.equal(window.formatMeasure(5 * ONE_DAY, 'WORK_DUR'), '5d'); - assert.equal(window.formatMeasure(2 * ONE_HOUR, 'WORK_DUR'), '2h'); - assert.equal(window.formatMeasure(ONE_MINUTE, 'WORK_DUR'), '1min'); - assert.equal(window.formatMeasure(5 * ONE_DAY + 2 * ONE_HOUR, 'WORK_DUR'), '5d 2h'); - assert.equal(window.formatMeasure(2 * ONE_HOUR + ONE_MINUTE, 'WORK_DUR'), '2h 1min'); - assert.equal(window.formatMeasure(5 * ONE_DAY + 2 * ONE_HOUR + ONE_MINUTE, 'WORK_DUR'), '5d 2h'); - assert.equal(window.formatMeasure(15 * ONE_DAY + 2 * ONE_HOUR + ONE_MINUTE, 'WORK_DUR'), '15d'); - assert.equal(window.formatMeasure(-5 * ONE_DAY, 'WORK_DUR'), '-5d'); - assert.equal(window.formatMeasure(-2 * ONE_HOUR, 'WORK_DUR'), '-2h'); - assert.equal(window.formatMeasure(-1 * ONE_MINUTE, 'WORK_DUR'), '-1min'); - }); - - it('should format SHORT_WORK_DUR', function () { - assert.equal(window.formatMeasure(0, 'SHORT_WORK_DUR'), '0'); - assert.equal(window.formatMeasure(5 * ONE_DAY, 'SHORT_WORK_DUR'), '5d'); - assert.equal(window.formatMeasure(2 * ONE_HOUR, 'SHORT_WORK_DUR'), '2h'); - assert.equal(window.formatMeasure(ONE_MINUTE, 'SHORT_WORK_DUR'), '1min'); - assert.equal(window.formatMeasure(5 * ONE_DAY + 2 * ONE_HOUR, 'SHORT_WORK_DUR'), '~ 5d'); - assert.equal(window.formatMeasure(2 * ONE_HOUR + ONE_MINUTE, 'SHORT_WORK_DUR'), '~ 2h'); - assert.equal(window.formatMeasure(5 * ONE_DAY + 2 * ONE_HOUR + ONE_MINUTE, 'SHORT_WORK_DUR'), '~ 5d'); - assert.equal(window.formatMeasure(15 * ONE_DAY + 2 * ONE_HOUR + ONE_MINUTE, 'SHORT_WORK_DUR'), '~ 15d'); - assert.equal(window.formatMeasure(7 * ONE_MINUTE, 'SHORT_WORK_DUR'), '7min'); - assert.equal(window.formatMeasure(-5 * ONE_DAY, 'SHORT_WORK_DUR'), '-5d'); - assert.equal(window.formatMeasure(-2 * ONE_HOUR, 'SHORT_WORK_DUR'), '-2h'); - assert.equal(window.formatMeasure(-1 * ONE_MINUTE, 'SHORT_WORK_DUR'), '-1min'); - - assert.equal(window.formatMeasure(1529 * ONE_DAY, 'SHORT_WORK_DUR'), '1.5kd'); - assert.equal(window.formatMeasure(1234567 * ONE_DAY, 'SHORT_WORK_DUR'), '1md'); - assert.equal(window.formatMeasure(1234567 * ONE_DAY + 2 * ONE_HOUR, 'SHORT_WORK_DUR'), '1md'); - }); - - it('should format RATING', function () { - assert.equal(window.formatMeasure(1, 'RATING'), 'A'); - assert.equal(window.formatMeasure(2, 'RATING'), 'B'); - assert.equal(window.formatMeasure(3, 'RATING'), 'C'); - assert.equal(window.formatMeasure(4, 'RATING'), 'D'); - assert.equal(window.formatMeasure(5, 'RATING'), 'E'); - }); - - it('should not format unknown type', function () { - assert.equal(window.formatMeasure('random value', 'RANDOM_TYPE'), 'random value'); - }); - - it('should not fail without parameters', function () { - assert.isNull(window.formatMeasure()); - }); - }); - - describe('#formatMeasureVariation()', function () { - it('should format INT', function () { - assert.equal(window.formatMeasureVariation(0, 'INT'), '0'); - assert.equal(window.formatMeasureVariation(1, 'INT'), '+1'); - assert.equal(window.formatMeasureVariation(-1, 'INT'), '-1'); - assert.equal(window.formatMeasureVariation(1529, 'INT'), '+1,529'); - assert.equal(window.formatMeasureVariation(-1529, 'INT'), '-1,529'); - }); - - it('should format SHORT_INT', function () { - assert.equal(window.formatMeasureVariation(0, 'SHORT_INT'), '0'); - assert.equal(window.formatMeasureVariation(1, 'SHORT_INT'), '+1'); - assert.equal(window.formatMeasureVariation(-1, 'SHORT_INT'), '-1'); - assert.equal(window.formatMeasureVariation(1529, 'SHORT_INT'), '+1.5k'); - assert.equal(window.formatMeasureVariation(-1529, 'SHORT_INT'), '-1.5k'); - assert.equal(window.formatMeasureVariation(10678, 'SHORT_INT'), '+11k'); - assert.equal(window.formatMeasureVariation(-10678, 'SHORT_INT'), '-11k'); - }); - - it('should format FLOAT', function () { - assert.equal(window.formatMeasureVariation(0.0, 'FLOAT'), '0'); - assert.equal(window.formatMeasureVariation(1.0, 'FLOAT'), '+1.0'); - assert.equal(window.formatMeasureVariation(-1.0, 'FLOAT'), '-1.0'); - assert.equal(window.formatMeasureVariation(50.89, 'FLOAT'), '+50.9'); - assert.equal(window.formatMeasureVariation(-50.89, 'FLOAT'), '-50.9'); - }); - - it('should format PERCENT', function () { - assert.equal(window.formatMeasureVariation(0.0, 'PERCENT'), '0%'); - assert.equal(window.formatMeasureVariation(1.0, 'PERCENT'), '+1.0%'); - assert.equal(window.formatMeasureVariation(-1.0, 'PERCENT'), '-1.0%'); - assert.equal(window.formatMeasureVariation(50.89, 'PERCENT'), '+50.9%'); - assert.equal(window.formatMeasureVariation(-50.89, 'PERCENT'), '-50.9%'); - }); - - it('should format WORK_DUR', function () { - assert.equal(window.formatMeasureVariation(0, 'WORK_DUR'), '0'); - assert.equal(window.formatMeasureVariation(5 * ONE_DAY, 'WORK_DUR'), '+5d'); - assert.equal(window.formatMeasureVariation(2 * ONE_HOUR, 'WORK_DUR'), '+2h'); - assert.equal(window.formatMeasureVariation(ONE_MINUTE, 'WORK_DUR'), '+1min'); - assert.equal(window.formatMeasureVariation(-5 * ONE_DAY, 'WORK_DUR'), '-5d'); - assert.equal(window.formatMeasureVariation(-2 * ONE_HOUR, 'WORK_DUR'), '-2h'); - assert.equal(window.formatMeasureVariation(-1 * ONE_MINUTE, 'WORK_DUR'), '-1min'); - }); - - it('should not format unknown type', function () { - assert.equal(window.formatMeasureVariation('random value', 'RANDOM_TYPE'), 'random value'); - }); - - it('should not fail without parameters', function () { - assert.isNull(window.formatMeasureVariation()); - }); - }); - }); - describe('Severity Comparators', function () { describe('#severityComparator', function () { it('should have correct order', function () { diff --git a/server/sonar-web/tests/helpers/measures-test.js b/server/sonar-web/tests/helpers/measures-test.js new file mode 100644 index 00000000000..e302e2db0ab --- /dev/null +++ b/server/sonar-web/tests/helpers/measures-test.js @@ -0,0 +1,226 @@ +import { expect } from 'chai'; + +import { formatMeasure, formatMeasureVariation } from '../../src/main/js/helpers/measures'; + + +describe('Measures', function () { + var HOURS_IN_DAY = 8, + ONE_MINUTE = 1, + ONE_HOUR = ONE_MINUTE * 60, + ONE_DAY = HOURS_IN_DAY * ONE_HOUR; + + before(function () { + window.messages = { + 'work_duration.x_days': '{0}d', + 'work_duration.x_hours': '{0}h', + 'work_duration.x_minutes': '{0}min', + 'work_duration.about': '~ {0}', + 'metric.level.ERROR': 'Error', + 'metric.level.WARN': 'Warning', + 'metric.level.OK': 'Ok' + }; + window.t = function() { + if (!window.messages) { + return window.translate.apply(this, arguments); + } + var args = Array.prototype.slice.call(arguments, 0), + key = args.join('.'); + return window.messages[key] != null ? window.messages[key] : key; + }; + window.tp = function () { + var args = Array.prototype.slice.call(arguments, 0), + key = args.shift(), + message = window.messages[key]; + if (message) { + args.forEach(function (p, i) { + message = message.replace('{' + i + '}', p); + }); + } + return message || (key + ' ' + args.join(' ')); + }; + window.SS = { hoursInDay: HOURS_IN_DAY }; + }); + + describe('#formatMeasure()', function () { + it('should format INT', function () { + expect(formatMeasure(0, 'INT')).to.equal('0'); + expect(formatMeasure(1, 'INT')).to.equal('1'); + expect(formatMeasure(-5, 'INT')).to.equal('-5'); + expect(formatMeasure(999, 'INT')).to.equal('999'); + expect(formatMeasure(1000, 'INT')).to.equal('1,000'); + expect(formatMeasure(1529, 'INT')).to.equal('1,529'); + expect(formatMeasure(10000, 'INT')).to.equal('10,000'); + expect(formatMeasure(1234567890, 'INT')).to.equal('1,234,567,890'); + }); + + it('should format SHORT_INT', function () { + expect(formatMeasure(0, 'SHORT_INT')).to.equal('0'); + expect(formatMeasure(1, 'SHORT_INT')).to.equal('1'); + expect(formatMeasure(999, 'SHORT_INT')).to.equal('999'); + expect(formatMeasure(1000, 'SHORT_INT')).to.equal('1k'); + expect(formatMeasure(1529, 'SHORT_INT')).to.equal('1.5k'); + expect(formatMeasure(10000, 'SHORT_INT')).to.equal('10k'); + expect(formatMeasure(10678, 'SHORT_INT')).to.equal('11k'); + expect(formatMeasure(1234567890, 'SHORT_INT')).to.equal('1b'); + }); + + it('should format FLOAT', function () { + expect(formatMeasure(0.0, 'FLOAT')).to.equal('0.0'); + expect(formatMeasure(1.0, 'FLOAT')).to.equal('1.0'); + expect(formatMeasure(1.3, 'FLOAT')).to.equal('1.3'); + expect(formatMeasure(1.34, 'FLOAT')).to.equal('1.3'); + expect(formatMeasure(50.89, 'FLOAT')).to.equal('50.9'); + expect(formatMeasure(100.0, 'FLOAT')).to.equal('100.0'); + expect(formatMeasure(123.456, 'FLOAT')).to.equal('123.5'); + expect(formatMeasure(123456.7, 'FLOAT')).to.equal('123,456.7'); + expect(formatMeasure(1234567890.0, 'FLOAT')).to.equal('1,234,567,890.0'); + }); + + it('should format PERCENT', function () { + expect(formatMeasure(0.0, 'PERCENT')).to.equal('0.0%'); + expect(formatMeasure(1.0, 'PERCENT')).to.equal('1.0%'); + expect(formatMeasure(1.3, 'PERCENT')).to.equal('1.3%'); + expect(formatMeasure(1.34, 'PERCENT')).to.equal('1.3%'); + expect(formatMeasure(50.89, 'PERCENT')).to.equal('50.9%'); + expect(formatMeasure(100.0, 'PERCENT')).to.equal('100.0%'); + }); + + it('should format WORK_DUR', function () { + expect(formatMeasure(0, 'WORK_DUR')).to.equal('0'); + expect(formatMeasure(5 * ONE_DAY, 'WORK_DUR')).to.equal('5d'); + expect(formatMeasure(2 * ONE_HOUR, 'WORK_DUR')).to.equal('2h'); + expect(formatMeasure(ONE_MINUTE, 'WORK_DUR')).to.equal('1min'); + expect(formatMeasure(5 * ONE_DAY + 2 * ONE_HOUR, 'WORK_DUR')).to.equal('5d 2h'); + expect(formatMeasure(2 * ONE_HOUR + ONE_MINUTE, 'WORK_DUR')).to.equal('2h 1min'); + expect(formatMeasure(5 * ONE_DAY + 2 * ONE_HOUR + ONE_MINUTE, 'WORK_DUR')).to.equal('5d 2h'); + expect(formatMeasure(15 * ONE_DAY + 2 * ONE_HOUR + ONE_MINUTE, 'WORK_DUR')).to.equal('15d'); + expect(formatMeasure(-5 * ONE_DAY, 'WORK_DUR')).to.equal('-5d'); + expect(formatMeasure(-2 * ONE_HOUR, 'WORK_DUR')).to.equal('-2h'); + expect(formatMeasure(-1 * ONE_MINUTE, 'WORK_DUR')).to.equal('-1min'); + }); + + it('should format SHORT_WORK_DUR', function () { + expect(formatMeasure(0, 'SHORT_WORK_DUR')).to.equal('0'); + expect(formatMeasure(5 * ONE_DAY, 'SHORT_WORK_DUR')).to.equal('5d'); + expect(formatMeasure(2 * ONE_HOUR, 'SHORT_WORK_DUR')).to.equal('2h'); + expect(formatMeasure(ONE_MINUTE, 'SHORT_WORK_DUR')).to.equal('1min'); + expect(formatMeasure(5 * ONE_DAY + 2 * ONE_HOUR, 'SHORT_WORK_DUR')).to.equal('5d'); + expect(formatMeasure(2 * ONE_HOUR + ONE_MINUTE, 'SHORT_WORK_DUR')).to.equal('2h'); + expect(formatMeasure(5 * ONE_DAY + 2 * ONE_HOUR + ONE_MINUTE, 'SHORT_WORK_DUR')).to.equal('5d'); + expect(formatMeasure(15 * ONE_DAY + 2 * ONE_HOUR + ONE_MINUTE, 'SHORT_WORK_DUR')).to.equal('15d'); + expect(formatMeasure(7 * ONE_MINUTE, 'SHORT_WORK_DUR')).to.equal('7min'); + expect(formatMeasure(-5 * ONE_DAY, 'SHORT_WORK_DUR')).to.equal('-5d'); + expect(formatMeasure(-2 * ONE_HOUR, 'SHORT_WORK_DUR')).to.equal('-2h'); + expect(formatMeasure(-1 * ONE_MINUTE, 'SHORT_WORK_DUR')).to.equal('-1min'); + + expect(formatMeasure(1529 * ONE_DAY, 'SHORT_WORK_DUR')).to.equal('1.5kd'); + expect(formatMeasure(1234567 * ONE_DAY, 'SHORT_WORK_DUR')).to.equal('1md'); + expect(formatMeasure(1234567 * ONE_DAY + 2 * ONE_HOUR, 'SHORT_WORK_DUR')).to.equal('1md'); + }); + + it('should format RATING', function () { + expect(formatMeasure(1, 'RATING')).to.equal('A'); + expect(formatMeasure(2, 'RATING')).to.equal('B'); + expect(formatMeasure(3, 'RATING')).to.equal('C'); + expect(formatMeasure(4, 'RATING')).to.equal('D'); + expect(formatMeasure(5, 'RATING')).to.equal('E'); + }); + + it('should format LEVEL', function () { + expect(formatMeasure('ERROR', 'LEVEL')).to.equal('Error'); + expect(formatMeasure('WARN', 'LEVEL')).to.equal('Warning'); + expect(formatMeasure('OK', 'LEVEL')).to.equal('Ok'); + expect(formatMeasure('UNKNOWN', 'LEVEL')).to.equal('UNKNOWN'); + }); + + it('should format MILLISEC', function () { + expect(formatMeasure(0, 'MILLISEC')).to.equal('0ms'); + expect(formatMeasure(1, 'MILLISEC')).to.equal('1ms'); + expect(formatMeasure(173, 'MILLISEC')).to.equal('173ms'); + expect(formatMeasure(3649, 'MILLISEC')).to.equal('4s'); + expect(formatMeasure(893481, 'MILLISEC')).to.equal('15min'); + expect(formatMeasure(17862325, 'MILLISEC')).to.equal('298min'); + }); + + it('should not format unknown type', function () { + expect(formatMeasure('random value', 'RANDOM_TYPE')).to.equal('random value'); + }); + + it('should not fail without parameters', function () { + expect(formatMeasure()).to.be.null; + }); + }); + + describe('#formatMeasureVariation()', function () { + it('should format INT', function () { + expect(formatMeasureVariation(0, 'INT')).to.equal('+0'); + expect(formatMeasureVariation(1, 'INT')).to.equal('+1'); + expect(formatMeasureVariation(-1, 'INT')).to.equal('-1'); + expect(formatMeasureVariation(1529, 'INT')).to.equal('+1,529'); + expect(formatMeasureVariation(-1529, 'INT')).to.equal('-1,529'); + }); + + it('should format SHORT_INT', function () { + expect(formatMeasureVariation(0, 'SHORT_INT')).to.equal('+0'); + expect(formatMeasureVariation(1, 'SHORT_INT')).to.equal('+1'); + expect(formatMeasureVariation(-1, 'SHORT_INT')).to.equal('-1'); + expect(formatMeasureVariation(1529, 'SHORT_INT')).to.equal('+1.5k'); + expect(formatMeasureVariation(-1529, 'SHORT_INT')).to.equal('-1.5k'); + expect(formatMeasureVariation(10678, 'SHORT_INT')).to.equal('+11k'); + expect(formatMeasureVariation(-10678, 'SHORT_INT')).to.equal('-11k'); + }); + + it('should format FLOAT', function () { + expect(formatMeasureVariation(0.0, 'FLOAT')).to.equal('+0.0'); + expect(formatMeasureVariation(1.0, 'FLOAT')).to.equal('+1.0'); + expect(formatMeasureVariation(-1.0, 'FLOAT')).to.equal('-1.0'); + expect(formatMeasureVariation(50.89, 'FLOAT')).to.equal('+50.9'); + expect(formatMeasureVariation(-50.89, 'FLOAT')).to.equal('-50.9'); + }); + + it('should format PERCENT', function () { + expect(formatMeasureVariation(0.0, 'PERCENT')).to.equal('+0.0%'); + expect(formatMeasureVariation(1.0, 'PERCENT')).to.equal('+1.0%'); + expect(formatMeasureVariation(-1.0, 'PERCENT')).to.equal('-1.0%'); + expect(formatMeasureVariation(50.89, 'PERCENT')).to.equal('+50.9%'); + expect(formatMeasureVariation(-50.89, 'PERCENT')).to.equal('-50.9%'); + }); + + it('should format WORK_DUR', function () { + expect(formatMeasureVariation(0, 'WORK_DUR')).to.equal('0'); + expect(formatMeasureVariation(5 * ONE_DAY, 'WORK_DUR')).to.equal('+5d'); + expect(formatMeasureVariation(2 * ONE_HOUR, 'WORK_DUR')).to.equal('+2h'); + expect(formatMeasureVariation(ONE_MINUTE, 'WORK_DUR')).to.equal('+1min'); + expect(formatMeasureVariation(-5 * ONE_DAY, 'WORK_DUR')).to.equal('-5d'); + expect(formatMeasureVariation(-2 * ONE_HOUR, 'WORK_DUR')).to.equal('-2h'); + expect(formatMeasureVariation(-1 * ONE_MINUTE, 'WORK_DUR')).to.equal('-1min'); + }); + + it('should format SHORT_WORK_DUR', function () { + expect(formatMeasureVariation(0, 'SHORT_WORK_DUR')).to.equal('+0'); + expect(formatMeasureVariation(5 * ONE_DAY, 'SHORT_WORK_DUR')).to.equal('+5d'); + expect(formatMeasureVariation(2 * ONE_HOUR, 'SHORT_WORK_DUR')).to.equal('+2h'); + expect(formatMeasureVariation(ONE_MINUTE, 'SHORT_WORK_DUR')).to.equal('+1min'); + expect(formatMeasureVariation(5 * ONE_DAY + 2 * ONE_HOUR, 'SHORT_WORK_DUR')).to.equal('+5d'); + expect(formatMeasureVariation(2 * ONE_HOUR + ONE_MINUTE, 'SHORT_WORK_DUR')).to.equal('+2h'); + expect(formatMeasureVariation(5 * ONE_DAY + 2 * ONE_HOUR + ONE_MINUTE, 'SHORT_WORK_DUR')).to.equal('+5d'); + expect(formatMeasureVariation(15 * ONE_DAY + 2 * ONE_HOUR + ONE_MINUTE, 'SHORT_WORK_DUR')).to.equal('+15d'); + expect(formatMeasureVariation(7 * ONE_MINUTE, 'SHORT_WORK_DUR')).to.equal('+7min'); + expect(formatMeasureVariation(-5 * ONE_DAY, 'SHORT_WORK_DUR')).to.equal('-5d'); + expect(formatMeasureVariation(-2 * ONE_HOUR, 'SHORT_WORK_DUR')).to.equal('-2h'); + expect(formatMeasureVariation(-1 * ONE_MINUTE, 'SHORT_WORK_DUR')).to.equal('-1min'); + + expect(formatMeasureVariation(1529 * ONE_DAY, 'SHORT_WORK_DUR')).to.equal('+1.5kd'); + expect(formatMeasureVariation(1234567 * ONE_DAY, 'SHORT_WORK_DUR')).to.equal('+1md'); + expect(formatMeasureVariation(1234567 * ONE_DAY + 2 * ONE_HOUR, 'SHORT_WORK_DUR')).to.equal('+1md'); + }); + + it('should not format unknown type', function () { + expect(formatMeasureVariation('random value', 'RANDOM_TYPE')).to.equal('random value'); + }); + + it('should not fail without parameters', function () { + expect(formatMeasureVariation()).to.be.null; + }); + }); +}); -- 2.39.5