aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/components/Measure.js9
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/utils.js6
-rw-r--r--server/sonar-web/src/main/js/apps/overview/qualityGate/QualityGateCondition.js23
-rw-r--r--server/sonar-web/src/main/js/apps/overview/qualityGate/__tests__/QualityGateCondition-test.js14
-rw-r--r--server/sonar-web/src/main/js/apps/overview/qualityGate/__tests__/__snapshots__/QualityGateCondition-test.js.snap8
-rw-r--r--server/sonar-web/src/main/js/helpers/__tests__/measures-test.js18
-rw-r--r--server/sonar-web/src/main/js/helpers/measures.js63
7 files changed, 106 insertions, 35 deletions
diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/Measure.js b/server/sonar-web/src/main/js/apps/component-measures/components/Measure.js
index 4700219e15b..520852b4367 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/components/Measure.js
+++ b/server/sonar-web/src/main/js/apps/component-measures/components/Measure.js
@@ -27,7 +27,8 @@ import { formatLeak, isDiffMetric, getRatingTooltip } from '../utils';
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) {
@@ -51,7 +52,7 @@ export default class Measure extends React.PureComponent {
}
render() {
- const { measure, metric } = this.props;
+ const { measure, metric, decimals } = this.props;
const finalMetric = metric || measure.metric;
if (finalMetric.type === 'RATING') {
@@ -63,8 +64,8 @@ export default class Measure extends React.PureComponent {
}
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>
diff --git a/server/sonar-web/src/main/js/apps/component-measures/utils.js b/server/sonar-web/src/main/js/apps/component-measures/utils.js
index 6b3abc7eda4..91c3e3f92b2 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/utils.js
+++ b/server/sonar-web/src/main/js/apps/component-measures/utils.js
@@ -62,11 +62,11 @@ export function getSingleLeakValue(measures, periodIndex = 1) {
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);
}
}
diff --git a/server/sonar-web/src/main/js/apps/overview/qualityGate/QualityGateCondition.js b/server/sonar-web/src/main/js/apps/overview/qualityGate/QualityGateCondition.js
index 1c0d73f31cb..d2ac1128fce 100644
--- a/server/sonar-web/src/main/js/apps/overview/qualityGate/QualityGateCondition.js
+++ b/server/sonar-web/src/main/js/apps/overview/qualityGate/QualityGateCondition.js
@@ -49,6 +49,14 @@ export default class QualityGateCondition extends React.PureComponent {
}
};
+ 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',
@@ -126,21 +134,24 @@ export default class QualityGateCondition extends React.PureComponent {
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>
diff --git a/server/sonar-web/src/main/js/apps/overview/qualityGate/__tests__/QualityGateCondition-test.js b/server/sonar-web/src/main/js/apps/overview/qualityGate/__tests__/QualityGateCondition-test.js
index 1b711f7c60c..449ceff035b 100644
--- a/server/sonar-web/src/main/js/apps/overview/qualityGate/__tests__/QualityGateCondition-test.js
+++ b/server/sonar-web/src/main/js/apps/overview/qualityGate/__tests__/QualityGateCondition-test.js
@@ -130,3 +130,17 @@ it('new_maintainability_rating', () => {
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);
+});
diff --git a/server/sonar-web/src/main/js/apps/overview/qualityGate/__tests__/__snapshots__/QualityGateCondition-test.js.snap b/server/sonar-web/src/main/js/apps/overview/qualityGate/__tests__/__snapshots__/QualityGateCondition-test.js.snap
index b392a10f224..f9e6d025176 100644
--- a/server/sonar-web/src/main/js/apps/overview/qualityGate/__tests__/__snapshots__/QualityGateCondition-test.js.snap
+++ b/server/sonar-web/src/main/js/apps/overview/qualityGate/__tests__/__snapshots__/QualityGateCondition-test.js.snap
@@ -24,6 +24,7 @@ exports[`new_maintainability_rating 1`] = `
className="overview-quality-gate-condition-value"
>
<Measure
+ decimals={null}
measure={
Object {
"leak": "3",
@@ -75,6 +76,7 @@ exports[`new_open_issues 1`] = `
className="overview-quality-gate-condition-value"
>
<Measure
+ decimals={null}
measure={
Object {
"leak": "10",
@@ -137,6 +139,7 @@ exports[`new_reliability_rating 1`] = `
className="overview-quality-gate-condition-value"
>
<Measure
+ decimals={null}
measure={
Object {
"leak": "3",
@@ -199,6 +202,7 @@ exports[`new_security_rating 1`] = `
className="overview-quality-gate-condition-value"
>
<Measure
+ decimals={null}
measure={
Object {
"leak": "3",
@@ -250,6 +254,7 @@ exports[`open_issues 1`] = `
className="overview-quality-gate-condition-value"
>
<Measure
+ decimals={null}
measure={
Object {
"leak": "10",
@@ -311,6 +316,7 @@ exports[`reliability_rating 1`] = `
className="overview-quality-gate-condition-value"
>
<Measure
+ decimals={null}
measure={
Object {
"leak": "3",
@@ -372,6 +378,7 @@ exports[`security_rating 1`] = `
className="overview-quality-gate-condition-value"
>
<Measure
+ decimals={null}
measure={
Object {
"leak": "3",
@@ -432,6 +439,7 @@ exports[`sqale_rating 1`] = `
className="overview-quality-gate-condition-value"
>
<Measure
+ decimals={null}
measure={
Object {
"leak": "3",
diff --git a/server/sonar-web/src/main/js/helpers/__tests__/measures-test.js b/server/sonar-web/src/main/js/helpers/__tests__/measures-test.js
index bc621a44fec..46d81f7f0a6 100644
--- a/server/sonar-web/src/main/js/helpers/__tests__/measures-test.js
+++ b/server/sonar-web/src/main/js/helpers/__tests__/measures-test.js
@@ -47,6 +47,8 @@ describe('#formatMeasure()', () => {
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', () => {
@@ -58,6 +60,8 @@ describe('#formatMeasure()', () => {
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', () => {
@@ -77,6 +81,14 @@ describe('#formatMeasure()', () => {
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', () => {
@@ -86,6 +98,12 @@ describe('#formatMeasure()', () => {
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', () => {
diff --git a/server/sonar-web/src/main/js/helpers/measures.js b/server/sonar-web/src/main/js/helpers/measures.js
index e665b5a809b..01c3c43a3ee 100644
--- a/server/sonar-web/src/main/js/helpers/measures.js
+++ b/server/sonar-web/src/main/js/helpers/measures.js
@@ -27,9 +27,9 @@ const HOURS_IN_DAY = 8;
* @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);
}
/**
@@ -37,9 +37,9 @@ export function formatMeasure(value, type) {
* @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);
}
/**
@@ -102,8 +102,8 @@ export function isDiffMetric(metricKey) {
* 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) {
@@ -140,6 +140,13 @@ function getVariationFormatter(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;
}
@@ -148,15 +155,15 @@ function emptyFormatter() {
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';
@@ -164,30 +171,42 @@ function shortIntFormatter(value) {
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) {