export default class Measure extends React.PureComponent {
static propTypes = {
measure: React.PropTypes.object,
- metric: React.PropTypes.object
+ metric: React.PropTypes.object,
+ decimals: React.PropTypes.number
};
renderRating(measure, metric) {
}
render() {
- const { measure, metric } = this.props;
+ const { measure, metric, decimals } = this.props;
const finalMetric = metric || measure.metric;
if (finalMetric.type === 'RATING') {
}
const formattedValue = isDiffMetric(finalMetric)
- ? formatLeak(measure.leak, finalMetric)
- : formatMeasure(measure.value, finalMetric.type);
+ ? formatLeak(measure.leak, finalMetric, { decimals })
+ : formatMeasure(measure.value, finalMetric.type, { decimals });
return (
<span>
return period ? period.value : null;
}
-export function formatLeak(value, metric) {
+export function formatLeak(value, metric, options) {
if (isDiffMetric(metric)) {
- return formatMeasure(value, metric.type);
+ return formatMeasure(value, metric.type, options);
} else {
- return formatMeasureVariation(value, metric.type);
+ return formatMeasureVariation(value, metric.type, options);
}
}
}
};
+ getDecimalsNumber(threshold: number, value: number) {
+ const delta = Math.abs(threshold - value);
+ if (delta < 0.1 && delta > 0) {
+ //$FlowFixMe The matching result can't null because of the previous check
+ return delta.toFixed(20).match('[^0\.]').index - 1;
+ }
+ }
+
getIssuesUrl(sinceLeakPeriod: boolean, customQuery: {}) {
const query: Object = {
resolved: 'false',
const { measure } = condition;
const { metric } = measure;
- const isRating = metric.type === 'RATING';
const isDiff = isDiffMetric(metric.key);
const threshold = condition.level === 'ERROR' ? condition.error : condition.warning;
-
const actual = condition.period ? getPeriodValue(measure, condition.period) : measure.value;
- const operator = isRating
- ? translate('quality_gates.operator', condition.op, 'rating')
- : translate('quality_gates.operator', condition.op);
+ let operator = translate('quality_gates.operator', condition.op);
+ let decimals = null;
+
+ if (metric.type === 'RATING') {
+ operator = translate('quality_gates.operator', condition.op, 'rating');
+ } else if (metric.type === 'PERCENT') {
+ decimals = this.getDecimalsNumber(parseFloat(condition.error), parseFloat(actual));
+ }
return this.wrapWithLink(
<div className="overview-quality-gate-condition-container">
<div className="overview-quality-gate-condition-value">
- <Measure measure={{ value: actual, leak: actual }} metric={metric} />
+ <Measure measure={{ value: actual, leak: actual }} metric={metric} decimals={decimals} />
</div>
<div>
shallow(<QualityGateCondition component={{ key: 'abcd-key' }} condition={condition} />)
).toMatchSnapshot();
});
+
+it('should be able to correctly decide how much decimals to show', () => {
+ const condition = mockRatingCondition('new_maintainability_rating');
+ const instance = shallow(
+ <QualityGateCondition component={{ key: 'abcd-key' }} condition={condition} />
+ ).instance();
+ expect(instance.getDecimalsNumber(85, 80)).toBe(undefined);
+ expect(instance.getDecimalsNumber(85, 85)).toBe(undefined);
+ expect(instance.getDecimalsNumber(85, 85.01)).toBe(2);
+ expect(instance.getDecimalsNumber(85, 84.95)).toBe(2);
+ expect(instance.getDecimalsNumber(85, 84.999999999999554)).toBe('9999999999995'.length);
+ expect(instance.getDecimalsNumber(85, 85.0000000000000954)).toBe('00000000000009'.length);
+ expect(instance.getDecimalsNumber(85, 85.00000000000000009)).toBe(undefined);
+});
className="overview-quality-gate-condition-value"
>
<Measure
+ decimals={null}
measure={
Object {
"leak": "3",
className="overview-quality-gate-condition-value"
>
<Measure
+ decimals={null}
measure={
Object {
"leak": "10",
className="overview-quality-gate-condition-value"
>
<Measure
+ decimals={null}
measure={
Object {
"leak": "3",
className="overview-quality-gate-condition-value"
>
<Measure
+ decimals={null}
measure={
Object {
"leak": "3",
className="overview-quality-gate-condition-value"
>
<Measure
+ decimals={null}
measure={
Object {
"leak": "10",
className="overview-quality-gate-condition-value"
>
<Measure
+ decimals={null}
measure={
Object {
"leak": "3",
className="overview-quality-gate-condition-value"
>
<Measure
+ decimals={null}
measure={
Object {
"leak": "3",
className="overview-quality-gate-condition-value"
>
<Measure
+ decimals={null}
measure={
Object {
"leak": "3",
expect(formatMeasure(1529, 'INT')).toBe('1,529');
expect(formatMeasure(10000, 'INT')).toBe('10,000');
expect(formatMeasure(1234567890, 'INT')).toBe('1,234,567,890');
+ expect(formatMeasure(1028.5, 'INT', { round: Math.floor })).toBe('1,028');
+ expect(formatMeasure(1028.5, 'INT', { round: Math.ceil })).toBe('1,029');
});
it('should format SHORT_INT', () => {
expect(formatMeasure(10000, 'SHORT_INT')).toBe('10k');
expect(formatMeasure(10678, 'SHORT_INT')).toBe('11k');
expect(formatMeasure(1234567890, 'SHORT_INT')).toBe('1b');
+ expect(formatMeasure(1529, 'SHORT_INT', { round: Math.ceil })).toBe('1.6k');
+ expect(formatMeasure(1589, 'SHORT_INT', { round: Math.floor })).toBe('1.5k');
});
it('should format FLOAT', () => {
expect(formatMeasure(0.12, 'FLOAT')).toBe('0.12');
expect(formatMeasure(0.12345, 'FLOAT')).toBe('0.12345');
expect(formatMeasure(0.123456, 'FLOAT')).toBe('0.12346');
+ expect(formatMeasure(0.123451, 'FLOAT', { round: Math.ceil })).toBe('0.12346');
+ expect(formatMeasure(0.123458, 'FLOAT', { round: Math.floor })).toBe('0.12345');
+ expect(formatMeasure(0.12345, 'FLOAT', { decimals: 1 })).toBe('0.1');
+ expect(formatMeasure(0.16789, 'FLOAT', { decimals: 1 })).toBe('0.2');
+ expect(formatMeasure(0.123456, 'FLOAT', { decimals: 5 })).toBe('0.12346');
+ expect(formatMeasure(0.1234567, 'FLOAT', { decimals: 6 })).toBe('0.123457');
+ expect(formatMeasure(0.12345, 'FLOAT', { decimals: 0 })).toBe('0.12345');
+ expect(formatMeasure(0.16789, 'FLOAT', { decimals: 1, round: Math.floor })).toBe('0.1');
});
it('should format PERCENT', () => {
expect(formatMeasure(1.34, 'PERCENT')).toBe('1.3%');
expect(formatMeasure(50.89, 'PERCENT')).toBe('50.9%');
expect(formatMeasure(100.0, 'PERCENT')).toBe('100%');
+ expect(formatMeasure(50.89, 'PERCENT', { round: Math.floor })).toBe('50.8%');
+ expect(formatMeasure(50.83, 'PERCENT', { round: Math.ceil })).toBe('50.9%');
+ expect(formatMeasure(50.89, 'PERCENT', { decimals: 1 })).toBe('50.9%');
+ expect(formatMeasure(50.89, 'PERCENT', { decimals: 1, round: Math.floor })).toBe('50.8%');
+ expect(formatMeasure(50.89, 'PERCENT', { decimals: 2 })).toBe('50.89%');
+ expect(formatMeasure(50.89, 'PERCENT', { decimals: 3 })).toBe('50.890%');
});
it('should format WORK_DUR', () => {
* @param {string|number} value
* @param {string} type
*/
-export function formatMeasure(value, type) {
+export function formatMeasure(value, type, options) {
const formatter = getFormatter(type);
- return useFormatter(value, formatter);
+ return useFormatter(value, formatter, options);
}
/**
* @param {string|number} value
* @param {string} type
*/
-export function formatMeasureVariation(value, type) {
+export function formatMeasureVariation(value, type, options) {
const formatter = getVariationFormatter(type);
- return useFormatter(value, formatter);
+ return useFormatter(value, formatter, options);
}
/**
* Helpers
*/
-function useFormatter(value, formatter) {
- return value != null && value !== '' && formatter != null ? formatter(value) : null;
+function useFormatter(value, formatter, options) {
+ return value != null && value !== '' && formatter != null ? formatter(value, options) : null;
}
function getFormatter(type) {
* Formatters
*/
+function genericFormatter(value, formatValue, options = {}) {
+ if (options.round) {
+ return numeral(value).format(formatValue, options.round);
+ }
+ return numeral(value).format(formatValue);
+}
+
function noFormatter(value) {
return value;
}
return null;
}
-function intFormatter(value) {
- return numeral(value).format('0,0');
+function intFormatter(value, options) {
+ return genericFormatter(value, '0,0', options);
}
-function intVariationFormatter(value) {
- return numeral(value).format('+0,0');
+function intVariationFormatter(value, options) {
+ return genericFormatter(value, '+0,0', options);
}
-function shortIntFormatter(value) {
+function shortIntFormatter(value, options) {
let format = '0,0';
if (value >= 1000) {
format = '0.[0]a';
if (value >= 10000) {
format = '0a';
}
- return numeral(value).format(format);
+ return genericFormatter(value, format, options);
}
-function shortIntVariationFormatter(value) {
- const formatted = shortIntFormatter(Math.abs(value));
+function shortIntVariationFormatter(value, options) {
+ const formatted = shortIntFormatter(Math.abs(value), options);
return value < 0 ? `-${formatted}` : `+${formatted}`;
}
-function floatFormatter(value) {
- return numeral(value).format('0,0.0[0000]');
+function floatFormatter(value, options = {}) {
+ if (options.decimals) {
+ return genericFormatter(value, `0,0.${'0'.repeat(options.decimals)}`, options);
+ }
+ return genericFormatter(value, '0,0.0[0000]', options);
}
-function floatVariationFormatter(value) {
- return value === 0 ? '+0.0' : numeral(value).format('+0,0.0[0000]');
+function floatVariationFormatter(value, options = {}) {
+ if (options.decimals) {
+ return genericFormatter(value, `+0,0.${'0'.repeat(options.decimals)}`, options);
+ }
+ return value === 0 ? '+0.0' : genericFormatter(value, '+0,0.0[0000]', options);
}
-function percentFormatter(value) {
+function percentFormatter(value, options = {}) {
value = parseFloat(value);
- return value === 100 ? '100%' : numeral(value / 100).format('0,0.0%');
+ if (options.decimals) {
+ return genericFormatter(value / 100, `0,0.${'0'.repeat(options.decimals)}%`, options);
+ }
+ return value === 100 ? '100%' : genericFormatter(value / 100, '0,0.0%', options);
}
-function percentVariationFormatter(value) {
+function percentVariationFormatter(value, options = {}) {
value = parseFloat(value);
- return value === 0 ? '+0.0%' : numeral(value / 100).format('+0,0.0%');
+ if (options.decimals) {
+ return genericFormatter(value / 100, `+0,0.${'0'.repeat(options.decimals)}%`, options);
+ }
+ return value === 0 ? '+0.0%' : genericFormatter(value / 100, '+0,0.0%', options);
}
function ratingFormatter(value) {