"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",
"browserify-shim": {
"jquery": "global:jQuery",
"underscore": "global:_",
- "d3": "global:d3"
+ "d3": "global:d3",
+ "numeral": "global:numeral"
},
"browserify": {
"transform": [
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,
}
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);
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);
});
import { getMeasures } from '../../../api/measures';
import DrilldownLink from '../helpers/drilldown-link';
+import { formatMeasure } from '../../../helpers/measures';
const METRICS = [
renderValue (value, metricKey) {
if (value != null) {
return <DrilldownLink component={this.props.component.key} metric={metricKey}>
- {window.formatMeasure(value, 'PERCENT')}
+ {formatMeasure(value, 'PERCENT')}
</DrilldownLink>;
} else {
return '—';
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;
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('<br>');
return `<div class="text-left">${inner}</div>`;
}
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 <BubbleChart items={items}
height={HEIGHT}
padding={[25, 30, 50, 60]}
import DrilldownLink from '../helpers/drilldown-link';
import { getMeasures } from '../../../api/measures';
+import { formatMeasure } from '../../../helpers/measures';
export class DomainMeasuresList extends React.Component {
renderValue (value, metricKey, metricType) {
if (value != null) {
return <DrilldownLink component={this.props.component.key} metric={metricKey}>
- {window.formatMeasure(value, metricType)}
+ {formatMeasure(value, metricType)}
</DrilldownLink>;
} else {
return '—';
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;
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
import { Treemap } from '../../../components/charts/treemap';
import { getChildren } from '../../../api/components';
+import { formatMeasure } from '../../../helpers/measures';
const HEIGHT = 360;
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('<br>');
return `<div class="text-left">${inner}</div>`;
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({
renderNewCoverage () {
if (this.props.leak['new_overall_coverage'] != null) {
return <DrilldownLink component={this.props.component.key} metric="new_overall_coverage" period="1">
- {window.formatMeasure(this.props.leak['new_overall_coverage'], 'PERCENT')}
+ {formatMeasure(this.props.leak['new_overall_coverage'], 'PERCENT')}
</DrilldownLink>;
} else {
return <span>—</span>;
<MeasuresList>
<Measure label={getMetricName('coverage')}>
<DrilldownLink component={this.props.component.key} metric="overall_coverage">
- {window.formatMeasure(this.props.measures['overall_coverage'], 'PERCENT')}
+ {formatMeasure(this.props.measures['overall_coverage'], 'PERCENT')}
</DrilldownLink>
</Measure>
<Measure label={getMetricName('tests')}>
<DrilldownLink component={this.props.component.key} metric="tests">
- {window.formatMeasure(this.props.measures['tests'], 'SHORT_INT')}
+ {formatMeasure(this.props.measures['tests'], 'SHORT_INT')}
</DrilldownLink>
</Measure>
</MeasuresList>
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({
return <DomainLeak>
<MeasuresList>
<Measure label={getMetricName('duplications')}>
- {window.formatMeasureVariation(this.props.leak['duplicated_lines_density'], 'PERCENT')}
+ {formatMeasureVariation(this.props.leak['duplicated_lines_density'], 'PERCENT')}
</Measure>
</MeasuresList>
{this.renderTimeline('after')}
<MeasuresList>
<Measure label={getMetricName('duplications')}>
<DrilldownLink component={this.props.component.key} metric="duplicated_lines_density">
- {window.formatMeasure(this.props.measures['duplicated_lines_density'], 'PERCENT')}
+ {formatMeasure(this.props.measures['duplicated_lines_density'], 'PERCENT')}
</DrilldownLink>
</Measure>
<Measure label={getMetricName('duplicated_blocks')}>
<DrilldownLink component={this.props.component.key} metric="duplicated_blocks">
- {window.formatMeasure(this.props.measures['duplicated_blocks'], 'SHORT_INT')}
+ {formatMeasure(this.props.measures['duplicated_blocks'], 'SHORT_INT')}
</DrilldownLink>
</Measure>
</MeasuresList>
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({
</td>
<td className="thin nowrap text-right">
<IssuesLink component={this.props.component.key} params={{ resolved: 'false', severities: s }}>
- {window.formatMeasure(measure, 'SHORT_INT')}
+ {formatMeasure(measure, 'SHORT_INT')}
</IssuesLink>
</td>
</tr>;
<Measure label={getMetricName('new_issues')}>
<IssuesLink component={this.props.component.key}
params={{ resolved: 'false', createdAfter: createdAfter }}>
- {window.formatMeasureVariation(this.props.leak.issues, 'SHORT_INT')}
+ {formatMeasureVariation(this.props.leak.issues, 'SHORT_INT')}
</IssuesLink>
</Measure>
<Measure label={getMetricName('new_debt')}>
<IssuesLink component={this.props.component.key}
params={{ resolved: 'false', createdAfter: createdAfter, facetMode: 'debt' }}>
- {window.formatMeasureVariation(this.props.leak.debt, 'SHORT_WORK_DUR')}
+ {formatMeasureVariation(this.props.leak.debt, 'SHORT_WORK_DUR')}
</IssuesLink>
</Measure>
</MeasuresList>
<span className="spacer-right"><SeverityIcon severity="BLOCKER"/></span>
<IssuesLink component={this.props.component.key}
params={{ resolved: 'false', severities: 'BLOCKER', createdAfter: createdAfter }}>
- {window.formatMeasureVariation(this.props.leak.issuesSeverities[0], 'SHORT_INT')}
+ {formatMeasureVariation(this.props.leak.issuesSeverities[0], 'SHORT_INT')}
</IssuesLink>
</Measure>
<Measure label={getMetricName('new_critical_issues')}>
<span className="spacer-right"><SeverityIcon severity="CRITICAL"/></span>
<IssuesLink component={this.props.component.key}
params={{ resolved: 'false', severities: 'CRITICAL', createdAfter: createdAfter }}>
- {window.formatMeasureVariation(this.props.leak.issuesSeverities[1], 'SHORT_INT')}
+ {formatMeasureVariation(this.props.leak.issuesSeverities[1], 'SHORT_INT')}
</IssuesLink>
</Measure>
<Measure label={getMetricName('new_open_issues')}>
<span className="spacer-right"><StatusIcon status="OPEN"/></span>
<IssuesLink component={this.props.component.key}
params={{ resolved: 'false', statuses: 'OPEN,REOPENED', createdAfter: createdAfter }}>
- {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')}
</IssuesLink>
</Measure>
</MeasuresList>
</Measure>
<Measure label={getMetricName('issues')}>
<IssuesLink component={this.props.component.key} params={{ resolved: 'false' }}>
- {window.formatMeasure(this.props.measures.issues, 'SHORT_INT')}
+ {formatMeasure(this.props.measures.issues, 'SHORT_INT')}
</IssuesLink>
</Measure>
<Measure label={getMetricName('debt')}>
<IssuesLink component={this.props.component.key} params={{ resolved: 'false', facetMode: 'debt' }}>
- {window.formatMeasure(this.props.measures.debt, 'SHORT_WORK_DUR')}
+ {formatMeasure(this.props.measures.debt, 'SHORT_WORK_DUR')}
</IssuesLink>
</Measure>
<Measure composite={true}>
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({
return <DomainLeak>
<MeasuresList>
<Measure label={getMetricName('ncloc')}>
- {window.formatMeasureVariation(this.props.leak['ncloc'], 'SHORT_INT')}
+ {formatMeasureVariation(this.props.leak['ncloc'], 'SHORT_INT')}
</Measure>
<Measure label={getMetricName('files')}>
- {window.formatMeasureVariation(this.props.leak['files'], 'SHORT_INT')}
+ {formatMeasureVariation(this.props.leak['files'], 'SHORT_INT')}
</Measure>
</MeasuresList>
{this.renderTimeline('after')}
<MeasuresList>
<Measure label={getMetricName('ncloc')}>
<DrilldownLink component={this.props.component.key} metric="ncloc">
- {window.formatMeasure(this.props.measures['ncloc'], 'SHORT_INT')}
+ {formatMeasure(this.props.measures['ncloc'], 'SHORT_INT')}
</DrilldownLink>
</Measure>
<Measure label={getMetricName('files')}>
<DrilldownLink component={this.props.component.key} metric="files">
- {window.formatMeasure(this.props.measures['files'], 'SHORT_INT')}
+ {formatMeasure(this.props.measures['files'], 'SHORT_INT')}
</DrilldownLink>
</Measure>
</MeasuresList>
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 <span>{formatted}</span>;
}
});
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 <span>{formatted}</span>;
}
});
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 <span className={className}>{formatted}</span>;
}
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 () {
<Assignee user={s.user}/>
</td>
<td className="thin text-right">
- <a href={href}>{window.formatMeasure(s.count, 'SHORT_INT')}</a>
+ <a href={href}>{formatMeasure(s.count, 'SHORT_INT')}</a>
</td>
</tr>;
});
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 () {
</td>
<td className="thin text-right">
<a className="cell-link" href={href}>
- {window.formatMeasure(s.count, 'SHORT_INT')}
+ {formatMeasure(s.count, 'SHORT_INT')}
</a>
</td>
</tr>;
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 <WordCloud items={tags}/>;
import { BarChart } from '../../../components/charts/bar-chart';
import { getMeasures } from '../../../api/measures';
+import { formatMeasure } from '../../../helpers/measures';
const HEIGHT = 120;
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 <BarChart data={data}
xTicks={xTicks}
import { BarChart } from '../../../components/charts/bar-chart';
import { getMeasures } from '../../../api/measures';
import { getLanguages } from '../../../api/languages';
+import { formatMeasure } from '../../../helpers/measures';
const HEIGHT = 180;
let xTicks = data.map(d => this.getLanguageName(d.lang));
- let xValues = data.map(d => window.formatMeasure(d.y, 'INT'));
+ let xValues = data.map(d => formatMeasure(d.y, 'INT'));
return <BarChart data={data}
xTicks={xTicks}
import _ from 'underscore';
import Marionette from 'backbone.marionette';
import Template from './templates/quality-profiles-profile.hbs';
+import { formatMeasure } from '../../helpers/measures';
+
export default Marionette.ItemView.extend({
tagName: 'a',
serializeData: function () {
return _.extend(Marionette.ItemView.prototype.serializeData.apply(this, arguments), {
- projectCountFormatted: window.formatMeasure(this.model.get('projectCount'), 'INT')
+ projectCountFormatted: formatMeasure(this.model.get('projectCount'), 'INT')
});
}
});
import moment from 'moment';
import Handlebars from 'hbsfy/runtime';
import md5 from 'blueimp-md5';
+import { formatMeasure, formatMeasureVariation } from './measures';
+
var defaultActions = ['comment', 'assign', 'assign_to_me', 'plan', 'set_severity', 'set_tags'];
+
Handlebars.registerHelper('log', function () {
/* eslint no-console: 0 */
var args = Array.prototype.slice.call(arguments, 0, -1);
});
Handlebars.registerHelper('formatMeasure', function (measure, type) {
- return window.formatMeasure(measure, type);
+ return formatMeasure(measure, type);
});
Handlebars.registerHelper('formatMeasureVariation', function (measure, type) {
- return window.formatMeasureVariation(measure, type);
+ return formatMeasureVariation(measure, type);
});
Handlebars.registerHelper('dashboardL10n', function (dashboardName) {
--- /dev/null
+import numeral from 'numeral';
+
+
+/**
+ * Format a measure value for a given type
+ * @param {string|number} value
+ * @param {string} type
+ */
+export function formatMeasure (value, type) {
+ let formatter = getFormatter(type);
+ return useFormatter(value, formatter);
+}
+
+
+/**
+ * Format a measure variation for a given type
+ * @param {string|number} value
+ * @param {string} type
+ */
+export function formatMeasureVariation (value, type) {
+ let formatter = getVariationFormatter(type);
+ return useFormatter(value, formatter);
+}
+
+
+/*
+ * Helpers
+ */
+
+function useFormatter (value, formatter) {
+ return value != null && formatter != null ?
+ formatter(value) : null;
+}
+
+function getFormatter (type) {
+ const FORMATTERS = {
+ 'INT': intFormatter,
+ 'SHORT_INT': shortIntFormatter,
+ 'FLOAT': floatFormatter,
+ 'PERCENT': percentFormatter,
+ 'WORK_DUR': durationFormatter,
+ 'SHORT_WORK_DUR': shortDurationFormatter,
+ 'RATING': ratingFormatter,
+ 'LEVEL': levelFormatter,
+ 'MILLISEC': millisecondsFormatter
+ };
+ return FORMATTERS[type] || noFormatter;
+}
+
+function getVariationFormatter (type) {
+ const FORMATTERS = {
+ 'INT': intVariationFormatter,
+ 'SHORT_INT': shortIntVariationFormatter,
+ 'FLOAT': floatVariationFormatter,
+ 'PERCENT': percentVariationFormatter,
+ 'WORK_DUR': durationVariationFormatter,
+ 'SHORT_WORK_DUR': shortDurationVariationFormatter,
+ 'RATING': ratingFormatter,
+ 'LEVEL': levelFormatter,
+ 'MILLISEC': millisecondsFormatter
+ };
+ return FORMATTERS[type] || noFormatter;
+}
+
+
+/*
+ * Formatters
+ */
+
+
+function noFormatter (value) {
+ return value;
+}
+
+function intFormatter (value) {
+ return numeral(value).format('0,0');
+}
+
+function intVariationFormatter (value) {
+ return numeral(value).format('+0,0');
+}
+
+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) {
+ 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;
+}
-/*
- * 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
*/
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,
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);
});
});
- 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 () {
--- /dev/null
+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;
+ });
+ });
+});