Commit 794fd6b6 authored by Martin Wortschack's avatar Martin Wortschack

Merge branch 'more-helpful-number-formatting' into 'master'

Define easier to use interface for unit formatter

See merge request gitlab-org/gitlab!52668
parents 9ab32a71 3205f3c6
......@@ -2,7 +2,7 @@
import * as Sentry from '@sentry/browser';
import MetricCard from '~/analytics/shared/components/metric_card.vue';
import { deprecatedCreateFlash as createFlash } from '~/flash';
import { SUPPORTED_FORMATS, getFormatter } from '~/lib/utils/unit_format';
import { number } from '~/lib/utils/unit_format';
import { s__ } from '~/locale';
import usageTrendsCountQuery from '../graphql/queries/usage_trends_count.query.graphql';
......@@ -24,8 +24,7 @@ export default {
update(data) {
return Object.entries(data).map(([key, obj]) => {
const label = this.$options.i18n.labels[key];
const formatter = getFormatter(SUPPORTED_FORMATS.number);
const value = obj.nodes?.length ? formatter(obj.nodes[0].count, defaultPrecision) : null;
const value = obj.nodes?.length ? number(obj.nodes[0].count, defaultPrecision) : null;
return {
key,
......
import { formatNumber } from '~/locale';
/**
* Formats a number as string using `toLocaleString`.
* Formats a number as a string using `toLocaleString`.
*
* @param {Number} number to be converted
* @param {params} Parameters
* @param {params.fractionDigits} Number of decimal digits
* to display, defaults to using `toLocaleString` defaults.
* @param {params.maxLength} Max output char lenght at the
*
* @param {options.maxCharLength} Max output char length at the
* expense of precision, if the output is longer than this,
* the formatter switches to using exponential notation.
* @param {params.factor} Value is multiplied by this factor,
* useful for value normalization.
* @returns Formatted value
*
* @param {options.valueFactor} Value is multiplied by this factor,
* useful for value normalization or to alter orders of magnitude.
*
* @param {options} Other options to be passed to
* `formatNumber` such as `valueFactor`, `unit` and `style`.
*
*/
function formatNumber(
value,
{ fractionDigits = undefined, valueFactor = 1, style = undefined, maxLength = undefined },
) {
if (value === null) {
return '';
}
const locale = document.documentElement.lang || undefined;
const num = value * valueFactor;
const formatted = num.toLocaleString(locale, {
minimumFractionDigits: fractionDigits,
maximumFractionDigits: fractionDigits,
style,
});
const formatNumberNormalized = (value, { maxCharLength, valueFactor = 1, ...options }) => {
const formatted = formatNumber(value * valueFactor, options);
if (maxLength !== undefined && formatted.length > maxLength) {
if (maxCharLength !== undefined && formatted.length > maxCharLength) {
// 123456 becomes 1.23e+8
return num.toExponential(2);
return value.toExponential(2);
}
return formatted;
}
};
/**
* Formats a number as a string scaling it up according to units.
......@@ -76,7 +67,10 @@ const scaledFormatter = (units, unitFactor = 1000) => {
const unit = units[scale];
return `${formatNumber(num, { fractionDigits })}${unit}`;
return `${formatNumberNormalized(num, {
maximumFractionDigits: fractionDigits,
minimumFractionDigits: fractionDigits,
})}${unit}`;
};
};
......@@ -84,8 +78,14 @@ const scaledFormatter = (units, unitFactor = 1000) => {
* Returns a function that formats a number as a string.
*/
export const numberFormatter = (style = 'decimal', valueFactor = 1) => {
return (value, fractionDigits, maxLength) => {
return `${formatNumber(value, { fractionDigits, maxLength, valueFactor, style })}`;
return (value, fractionDigits, maxCharLength) => {
return `${formatNumberNormalized(value, {
maxCharLength,
valueFactor,
style,
maximumFractionDigits: fractionDigits,
minimumFractionDigits: fractionDigits,
})}`;
};
};
......@@ -93,9 +93,15 @@ export const numberFormatter = (style = 'decimal', valueFactor = 1) => {
* Returns a function that formats a number as a string with a suffix.
*/
export const suffixFormatter = (unit = '', valueFactor = 1) => {
return (value, fractionDigits, maxLength) => {
const length = maxLength !== undefined ? maxLength - unit.length : undefined;
return `${formatNumber(value, { fractionDigits, maxLength: length, valueFactor })}${unit}`;
return (value, fractionDigits, maxCharLength) => {
const length = maxCharLength !== undefined ? maxCharLength - unit.length : undefined;
return `${formatNumberNormalized(value, {
maxCharLength: length,
valueFactor,
maximumFractionDigits: fractionDigits,
minimumFractionDigits: fractionDigits,
})}${unit}`;
};
};
......
......@@ -46,227 +46,261 @@ export const SUPPORTED_FORMATS = {
};
/**
* Returns a function that formats number to different units
* @param {String} format - Format to use, must be one of the SUPPORTED_FORMATS. Defaults to engineering notation.
* Returns a function that formats number to different units.
*
* Used for dynamic formatting, for more convenience, use the functions below.
*
* @param {String} format - Format to use, must be one of the SUPPORTED_FORMATS. Defaults to engineering notation.
*/
export const getFormatter = (format = SUPPORTED_FORMATS.engineering) => {
// Number
if (format === SUPPORTED_FORMATS.number) {
/**
* Formats a number
*
* @function
* @param {Number} value - Number to format
* @param {Number} fractionDigits - precision decimals
* @param {Number} maxLength - Max length of formatted number
* if length is exceeded, exponential format is used.
*/
return numberFormatter();
}
if (format === SUPPORTED_FORMATS.percent) {
/**
* Formats a percentge (0 - 1)
*
* @function
* @param {Number} value - Number to format, `1` is rendered as `100%`
* @param {Number} fractionDigits - number of precision decimals
* @param {Number} maxLength - Max length of formatted number
* if length is exceeded, exponential format is used.
*/
return numberFormatter('percent');
}
if (format === SUPPORTED_FORMATS.percentHundred) {
/**
* Formats a percentge (0 to 100)
*
* @function
* @param {Number} value - Number to format, `100` is rendered as `100%`
* @param {Number} fractionDigits - number of precision decimals
* @param {Number} maxLength - Max length of formatted number
* if length is exceeded, exponential format is used.
*/
return numberFormatter('percent', 1 / 100);
}
// Durations
if (format === SUPPORTED_FORMATS.seconds) {
/**
* Formats a number of seconds
*
* @function
* @param {Number} value - Number to format, `1` is rendered as `1s`
* @param {Number} fractionDigits - number of precision decimals
* @param {Number} maxLength - Max length of formatted number
* if length is exceeded, exponential format is used.
*/
return suffixFormatter(s__('Units|s'));
}
if (format === SUPPORTED_FORMATS.milliseconds) {
/**
* Formats a number of milliseconds with ms as units
*
* @function
* @param {Number} value - Number to format, `1` is formatted as `1ms`
* @param {Number} fractionDigits - number of precision decimals
* @param {Number} maxLength - Max length of formatted number
* if length is exceeded, exponential format is used.
*/
return suffixFormatter(s__('Units|ms'));
}
// Digital (Metric)
if (format === SUPPORTED_FORMATS.decimalBytes) {
/**
* Formats a number of bytes scaled up to larger digital
* units for larger numbers.
*
* @function
* @param {Number} value - Number to format, `1` is formatted as `1B`
* @param {Number} fractionDigits - number of precision decimals
*/
return scaledSIFormatter('B');
}
if (format === SUPPORTED_FORMATS.kilobytes) {
/**
* Formats a number of kilobytes scaled up to larger digital
* units for larger numbers.
*
* @function
* @param {Number} value - Number to format, `1` is formatted as `1kB`
* @param {Number} fractionDigits - number of precision decimals
*/
return scaledSIFormatter('B', 1);
}
if (format === SUPPORTED_FORMATS.megabytes) {
/**
* Formats a number of megabytes scaled up to larger digital
* units for larger numbers.
*
* @function
* @param {Number} value - Number to format, `1` is formatted as `1MB`
* @param {Number} fractionDigits - number of precision decimals
*/
return scaledSIFormatter('B', 2);
}
if (format === SUPPORTED_FORMATS.gigabytes) {
/**
* Formats a number of gigabytes scaled up to larger digital
* units for larger numbers.
*
* @function
* @param {Number} value - Number to format, `1` is formatted as `1GB`
* @param {Number} fractionDigits - number of precision decimals
*/
return scaledSIFormatter('B', 3);
}
if (format === SUPPORTED_FORMATS.terabytes) {
/**
* Formats a number of terabytes scaled up to larger digital
* units for larger numbers.
*
* @function
* @param {Number} value - Number to format, `1` is formatted as `1GB`
* @param {Number} fractionDigits - number of precision decimals
*/
return scaledSIFormatter('B', 4);
}
if (format === SUPPORTED_FORMATS.petabytes) {
/**
* Formats a number of petabytes scaled up to larger digital
* units for larger numbers.
*
* @function
* @param {Number} value - Number to format, `1` is formatted as `1PB`
* @param {Number} fractionDigits - number of precision decimals
*/
return scaledSIFormatter('B', 5);
}
// Digital (IEC)
if (format === SUPPORTED_FORMATS.bytes) {
/**
* Formats a number of bytes scaled up to larger digital
* units for larger numbers.
*
* @function
* @param {Number} value - Number to format, `1` is formatted as `1B`
* @param {Number} fractionDigits - number of precision decimals
*/
return scaledBinaryFormatter('B');
}
if (format === SUPPORTED_FORMATS.kibibytes) {
/**
* Formats a number of kilobytes scaled up to larger digital
* units for larger numbers.
*
* @function
* @param {Number} value - Number to format, `1` is formatted as `1kB`
* @param {Number} fractionDigits - number of precision decimals
*/
return scaledBinaryFormatter('B', 1);
}
if (format === SUPPORTED_FORMATS.mebibytes) {
/**
* Formats a number of megabytes scaled up to larger digital
* units for larger numbers.
*
* @function
* @param {Number} value - Number to format, `1` is formatted as `1MB`
* @param {Number} fractionDigits - number of precision decimals
*/
return scaledBinaryFormatter('B', 2);
}
if (format === SUPPORTED_FORMATS.gibibytes) {
/**
* Formats a number of gigabytes scaled up to larger digital
* units for larger numbers.
*
* @function
* @param {Number} value - Number to format, `1` is formatted as `1GB`
* @param {Number} fractionDigits - number of precision decimals
*/
return scaledBinaryFormatter('B', 3);
}
if (format === SUPPORTED_FORMATS.tebibytes) {
/**
* Formats a number of terabytes scaled up to larger digital
* units for larger numbers.
*
* @function
* @param {Number} value - Number to format, `1` is formatted as `1GB`
* @param {Number} fractionDigits - number of precision decimals
*/
return scaledBinaryFormatter('B', 4);
}
if (format === SUPPORTED_FORMATS.pebibytes) {
/**
* Formats a number of petabytes scaled up to larger digital
* units for larger numbers.
*
* @function
* @param {Number} value - Number to format, `1` is formatted as `1PB`
* @param {Number} fractionDigits - number of precision decimals
*/
return scaledBinaryFormatter('B', 5);
}
// Default
if (format === SUPPORTED_FORMATS.engineering) {
/**
* Formats via engineering notation
*
* @function
* @param {Number} value - Value to format
* @param {Number} fractionDigits - precision decimals - Defaults to 2
*/
return engineeringNotation;
}
// Fail so client library addresses issue
throw TypeError(`${format} is not a valid number format`);
};
/**
* Formats a number
*
* @function
* @param {Number} value - Number to format
* @param {Number} fractionDigits - precision decimals
* @param {Number} maxLength - Max length of formatted number
* if length is exceeded, exponential format is used.
*/
export const number = getFormatter(SUPPORTED_FORMATS.number);
/**
* Formats a percentage (0 - 1)
*
* @function
* @param {Number} value - Number to format, `1` is rendered as `100%`
* @param {Number} fractionDigits - number of precision decimals
* @param {Number} maxLength - Max length of formatted number
* if length is exceeded, exponential format is used.
*/
export const percent = getFormatter(SUPPORTED_FORMATS.percent);
/**
* Formats a percentage (0 to 100)
*
* @function
* @param {Number} value - Number to format, `100` is rendered as `100%`
* @param {Number} fractionDigits - number of precision decimals
* @param {Number} maxLength - Max length of formatted number
* if length is exceeded, exponential format is used.
*/
export const percentHundred = getFormatter(SUPPORTED_FORMATS.percentHundred);
/**
* Formats a number of seconds
*
* @function
* @param {Number} value - Number to format, `1` is rendered as `1s`
* @param {Number} fractionDigits - number of precision decimals
* @param {Number} maxLength - Max length of formatted number
* if length is exceeded, exponential format is used.
*/
export const seconds = getFormatter(SUPPORTED_FORMATS.seconds);
/**
* Formats a number of milliseconds with ms as units
*
* @function
* @param {Number} value - Number to format, `1` is formatted as `1ms`
* @param {Number} fractionDigits - number of precision decimals
* @param {Number} maxLength - Max length of formatted number
* if length is exceeded, exponential format is used.
*/
export const milliseconds = getFormatter(SUPPORTED_FORMATS.milliseconds);
/**
* Formats a number of bytes scaled up to larger digital
* units for larger numbers.
*
* @function
* @param {Number} value - Number to format, `1` is formatted as `1B`
* @param {Number} fractionDigits - number of precision decimals
*/
export const decimalBytes = getFormatter(SUPPORTED_FORMATS.decimalBytes);
/**
* Formats a number of kilobytes scaled up to larger digital
* units for larger numbers.
*
* @function
* @param {Number} value - Number to format, `1` is formatted as `1kB`
* @param {Number} fractionDigits - number of precision decimals
*/
export const kilobytes = getFormatter(SUPPORTED_FORMATS.kilobytes);
/**
* Formats a number of megabytes scaled up to larger digital
* units for larger numbers.
*
* @function
* @param {Number} value - Number to format, `1` is formatted as `1MB`
* @param {Number} fractionDigits - number of precision decimals
*/
export const megabytes = getFormatter(SUPPORTED_FORMATS.megabytes);
/**
* Formats a number of gigabytes scaled up to larger digital
* units for larger numbers.
*
* @function
* @param {Number} value - Number to format, `1` is formatted as `1GB`
* @param {Number} fractionDigits - number of precision decimals
*/
export const gigabytes = getFormatter(SUPPORTED_FORMATS.gigabytes);
/**
* Formats a number of terabytes scaled up to larger digital
* units for larger numbers.
*
* @function
* @param {Number} value - Number to format, `1` is formatted as `1GB`
* @param {Number} fractionDigits - number of precision decimals
*/
export const terabytes = getFormatter(SUPPORTED_FORMATS.terabytes);
/**
* Formats a number of petabytes scaled up to larger digital
* units for larger numbers.
*
* @function
* @param {Number} value - Number to format, `1` is formatted as `1PB`
* @param {Number} fractionDigits - number of precision decimals
*/
export const petabytes = getFormatter(SUPPORTED_FORMATS.petabytes);
/**
* Formats a number of bytes scaled up to larger digital
* units for larger numbers.
*
* @function
* @param {Number} value - Number to format, `1` is formatted as `1B`
* @param {Number} fractionDigits - number of precision decimals
*/
export const bytes = getFormatter(SUPPORTED_FORMATS.bytes);
/**
* Formats a number of kilobytes scaled up to larger digital
* units for larger numbers.
*
* @function
* @param {Number} value - Number to format, `1` is formatted as `1kB`
* @param {Number} fractionDigits - number of precision decimals
*/
export const kibibytes = getFormatter(SUPPORTED_FORMATS.kibibytes);
/**
* Formats a number of megabytes scaled up to larger digital
* units for larger numbers.
*
* @function
* @param {Number} value - Number to format, `1` is formatted as `1MB`
* @param {Number} fractionDigits - number of precision decimals
*/
export const mebibytes = getFormatter(SUPPORTED_FORMATS.mebibytes);
/**
* Formats a number of gigabytes scaled up to larger digital
* units for larger numbers.
*
* @function
* @param {Number} value - Number to format, `1` is formatted as `1GB`
* @param {Number} fractionDigits - number of precision decimals
*/
export const gibibytes = getFormatter(SUPPORTED_FORMATS.gibibytes);
/**
* Formats a number of terabytes scaled up to larger digital
* units for larger numbers.
*
* @function
* @param {Number} value - Number to format, `1` is formatted as `1GB`
* @param {Number} fractionDigits - number of precision decimals
*/
export const tebibytes = getFormatter(SUPPORTED_FORMATS.tebibytes);
/**
* Formats a number of petabytes scaled up to larger digital
* units for larger numbers.
*
* @function
* @param {Number} value - Number to format, `1` is formatted as `1PB`
* @param {Number} fractionDigits - number of precision decimals
*/
export const pebibytes = getFormatter(SUPPORTED_FORMATS.pebibytes);
/**
* Formats via engineering notation
*
* @function
* @param {Number} value - Value to format
* @param {Number} fractionDigits - precision decimals - Defaults to 2
*/
export const engineering = getFormatter();
......@@ -58,10 +58,30 @@ const pgettext = (keyOrContext, key) => {
*/
const createDateTimeFormat = (formatOptions) => Intl.DateTimeFormat(languageCode(), formatOptions);
/**
* Formats a number as a string using `toLocaleString`.
*
* @param {Number} value - number to be converted
* @param {options?} options - options to be passed to
* `toLocaleString` such as `unit` and `style`.
* @param {langCode?} langCode - If set, forces a different
* language code from the one currently in the document.
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat/NumberFormat
*
* @returns If value is a number, the formatted value as a string
*/
function formatNumber(value, options = {}, langCode = languageCode()) {
if (typeof value !== 'number' && typeof value !== 'bigint') {
return value;
}
return value.toLocaleString(langCode, options);
}
export { languageCode };
export { gettext as __ };
export { ngettext as n__ };
export { pgettext as s__ };
export { sprintf };
export { createDateTimeFormat };
export { formatNumber };
export default locale;
import { getFormatter, SUPPORTED_FORMATS } from '~/lib/utils/unit_format';
import {
number,
percent,
percentHundred,
seconds,
milliseconds,
decimalBytes,
kilobytes,
megabytes,
gigabytes,
terabytes,
petabytes,
bytes,
kibibytes,
mebibytes,
gibibytes,
tebibytes,
pebibytes,
engineering,
getFormatter,
SUPPORTED_FORMATS,
} from '~/lib/utils/unit_format';
describe('unit_format', () => {
describe('when a supported format is provided, the returned function formats', () => {
it('numbers, by default', () => {
expect(getFormatter()(1)).toBe('1');
});
it('numbers', () => {
const formatNumber = getFormatter(SUPPORTED_FORMATS.number);
expect(formatNumber(1)).toBe('1');
expect(formatNumber(100)).toBe('100');
expect(formatNumber(1000)).toBe('1,000');
expect(formatNumber(10000)).toBe('10,000');
expect(formatNumber(1000000)).toBe('1,000,000');
});
it('percent', () => {
const formatPercent = getFormatter(SUPPORTED_FORMATS.percent);
it('engineering', () => {
expect(engineering(1)).toBe('1');
expect(engineering(100)).toBe('100');
expect(engineering(1000)).toBe('1k');
expect(engineering(10_000)).toBe('10k');
expect(engineering(1_000_000)).toBe('1M');
expect(engineering(10 ** 9)).toBe('1G');
});
expect(formatPercent(1)).toBe('100%');
expect(formatPercent(1, 2)).toBe('100.00%');
it('number', () => {
expect(number(1)).toBe('1');
expect(number(100)).toBe('100');
expect(number(1000)).toBe('1,000');
expect(number(10_000)).toBe('10,000');
expect(number(1_000_000)).toBe('1,000,000');
expect(formatPercent(0.1)).toBe('10%');
expect(formatPercent(0.5)).toBe('50%');
expect(number(10 ** 9)).toBe('1,000,000,000');
});
expect(formatPercent(0.888888)).toBe('89%');
expect(formatPercent(0.888888, 2)).toBe('88.89%');
expect(formatPercent(0.888888, 5)).toBe('88.88880%');
it('percent', () => {
expect(percent(1)).toBe('100%');
expect(percent(1, 2)).toBe('100.00%');
expect(formatPercent(2)).toBe('200%');
expect(formatPercent(10)).toBe('1,000%');
});
expect(percent(0.1)).toBe('10%');
expect(percent(0.5)).toBe('50%');
it('percentunit', () => {
const formatPercentHundred = getFormatter(SUPPORTED_FORMATS.percentHundred);
expect(percent(0.888888)).toBe('89%');
expect(percent(0.888888, 2)).toBe('88.89%');
expect(percent(0.888888, 5)).toBe('88.88880%');
expect(formatPercentHundred(1)).toBe('1%');
expect(formatPercentHundred(1, 2)).toBe('1.00%');
expect(formatPercentHundred(88.8888)).toBe('89%');
expect(formatPercentHundred(88.8888, 2)).toBe('88.89%');
expect(formatPercentHundred(88.8888, 5)).toBe('88.88880%');
expect(percent(2)).toBe('200%');
expect(percent(10)).toBe('1,000%');
});
expect(formatPercentHundred(100)).toBe('100%');
expect(formatPercentHundred(100, 2)).toBe('100.00%');
it('percentHundred', () => {
expect(percentHundred(1)).toBe('1%');
expect(percentHundred(1, 2)).toBe('1.00%');
expect(formatPercentHundred(200)).toBe('200%');
expect(formatPercentHundred(1000)).toBe('1,000%');
});
expect(percentHundred(88.8888)).toBe('89%');
expect(percentHundred(88.8888, 2)).toBe('88.89%');
expect(percentHundred(88.8888, 5)).toBe('88.88880%');
it('seconds', () => {
expect(getFormatter(SUPPORTED_FORMATS.seconds)(1)).toBe('1s');
});
expect(percentHundred(100)).toBe('100%');
expect(percentHundred(100, 2)).toBe('100.00%');
it('milliseconds', () => {
const formatMilliseconds = getFormatter(SUPPORTED_FORMATS.milliseconds);
expect(percentHundred(200)).toBe('200%');
expect(percentHundred(1000)).toBe('1,000%');
});
expect(formatMilliseconds(1)).toBe('1ms');
expect(formatMilliseconds(100)).toBe('100ms');
expect(formatMilliseconds(1000)).toBe('1,000ms');
expect(formatMilliseconds(10000)).toBe('10,000ms');
expect(formatMilliseconds(1000000)).toBe('1,000,000ms');
});
it('seconds', () => {
expect(seconds(1)).toBe('1s');
});
it('decimalBytes', () => {
const formatDecimalBytes = getFormatter(SUPPORTED_FORMATS.decimalBytes);
expect(formatDecimalBytes(1)).toBe('1B');
expect(formatDecimalBytes(1, 1)).toBe('1.0B');
expect(formatDecimalBytes(10)).toBe('10B');
expect(formatDecimalBytes(10 ** 2)).toBe('100B');
expect(formatDecimalBytes(10 ** 3)).toBe('1kB');
expect(formatDecimalBytes(10 ** 4)).toBe('10kB');
expect(formatDecimalBytes(10 ** 5)).toBe('100kB');
expect(formatDecimalBytes(10 ** 6)).toBe('1MB');
expect(formatDecimalBytes(10 ** 7)).toBe('10MB');
expect(formatDecimalBytes(10 ** 8)).toBe('100MB');
expect(formatDecimalBytes(10 ** 9)).toBe('1GB');
expect(formatDecimalBytes(10 ** 10)).toBe('10GB');
expect(formatDecimalBytes(10 ** 11)).toBe('100GB');
});
it('milliseconds', () => {
expect(milliseconds(1)).toBe('1ms');
expect(milliseconds(100)).toBe('100ms');
expect(milliseconds(1000)).toBe('1,000ms');
expect(milliseconds(10_000)).toBe('10,000ms');
expect(milliseconds(1_000_000)).toBe('1,000,000ms');
});
it('kilobytes', () => {
expect(getFormatter(SUPPORTED_FORMATS.kilobytes)(1)).toBe('1kB');
expect(getFormatter(SUPPORTED_FORMATS.kilobytes)(1, 1)).toBe('1.0kB');
});
it('decimalBytes', () => {
expect(decimalBytes(1)).toBe('1B');
expect(decimalBytes(1, 1)).toBe('1.0B');
expect(decimalBytes(10)).toBe('10B');
expect(decimalBytes(10 ** 2)).toBe('100B');
expect(decimalBytes(10 ** 3)).toBe('1kB');
expect(decimalBytes(10 ** 4)).toBe('10kB');
expect(decimalBytes(10 ** 5)).toBe('100kB');
expect(decimalBytes(10 ** 6)).toBe('1MB');
expect(decimalBytes(10 ** 7)).toBe('10MB');
expect(decimalBytes(10 ** 8)).toBe('100MB');
expect(decimalBytes(10 ** 9)).toBe('1GB');
expect(decimalBytes(10 ** 10)).toBe('10GB');
expect(decimalBytes(10 ** 11)).toBe('100GB');
});
it('megabytes', () => {
expect(getFormatter(SUPPORTED_FORMATS.megabytes)(1)).toBe('1MB');
expect(getFormatter(SUPPORTED_FORMATS.megabytes)(1, 1)).toBe('1.0MB');
});
it('kilobytes', () => {
expect(kilobytes(1)).toBe('1kB');
expect(kilobytes(1, 1)).toBe('1.0kB');
});
it('gigabytes', () => {
expect(getFormatter(SUPPORTED_FORMATS.gigabytes)(1)).toBe('1GB');
expect(getFormatter(SUPPORTED_FORMATS.gigabytes)(1, 1)).toBe('1.0GB');
});
it('megabytes', () => {
expect(megabytes(1)).toBe('1MB');
expect(megabytes(1, 1)).toBe('1.0MB');
});
it('terabytes', () => {
expect(getFormatter(SUPPORTED_FORMATS.terabytes)(1)).toBe('1TB');
expect(getFormatter(SUPPORTED_FORMATS.terabytes)(1, 1)).toBe('1.0TB');
});
it('gigabytes', () => {
expect(gigabytes(1)).toBe('1GB');
expect(gigabytes(1, 1)).toBe('1.0GB');
});
it('petabytes', () => {
expect(getFormatter(SUPPORTED_FORMATS.petabytes)(1)).toBe('1PB');
expect(getFormatter(SUPPORTED_FORMATS.petabytes)(1, 1)).toBe('1.0PB');
});
it('terabytes', () => {
expect(terabytes(1)).toBe('1TB');
expect(terabytes(1, 1)).toBe('1.0TB');
});
it('bytes', () => {
const formatBytes = getFormatter(SUPPORTED_FORMATS.bytes);
it('petabytes', () => {
expect(petabytes(1)).toBe('1PB');
expect(petabytes(1, 1)).toBe('1.0PB');
});
expect(formatBytes(1)).toBe('1B');
expect(formatBytes(1, 1)).toBe('1.0B');
it('bytes', () => {
expect(bytes(1)).toBe('1B');
expect(bytes(1, 1)).toBe('1.0B');
expect(formatBytes(10)).toBe('10B');
expect(formatBytes(100)).toBe('100B');
expect(formatBytes(1000)).toBe('1,000B');
expect(bytes(10)).toBe('10B');
expect(bytes(100)).toBe('100B');
expect(bytes(1000)).toBe('1,000B');
expect(formatBytes(1 * 1024)).toBe('1KiB');
expect(formatBytes(1 * 1024 ** 2)).toBe('1MiB');
expect(formatBytes(1 * 1024 ** 3)).toBe('1GiB');
});
expect(bytes(1 * 1024)).toBe('1KiB');
expect(bytes(1 * 1024 ** 2)).toBe('1MiB');
expect(bytes(1 * 1024 ** 3)).toBe('1GiB');
});
it('kibibytes', () => {
expect(getFormatter(SUPPORTED_FORMATS.kibibytes)(1)).toBe('1KiB');
expect(getFormatter(SUPPORTED_FORMATS.kibibytes)(1, 1)).toBe('1.0KiB');
});
it('kibibytes', () => {
expect(kibibytes(1)).toBe('1KiB');
expect(kibibytes(1, 1)).toBe('1.0KiB');
});
it('mebibytes', () => {
expect(getFormatter(SUPPORTED_FORMATS.mebibytes)(1)).toBe('1MiB');
expect(getFormatter(SUPPORTED_FORMATS.mebibytes)(1, 1)).toBe('1.0MiB');
});
it('mebibytes', () => {
expect(mebibytes(1)).toBe('1MiB');
expect(mebibytes(1, 1)).toBe('1.0MiB');
});
it('gibibytes', () => {
expect(getFormatter(SUPPORTED_FORMATS.gibibytes)(1)).toBe('1GiB');
expect(getFormatter(SUPPORTED_FORMATS.gibibytes)(1, 1)).toBe('1.0GiB');
});
it('gibibytes', () => {
expect(gibibytes(1)).toBe('1GiB');
expect(gibibytes(1, 1)).toBe('1.0GiB');
});
it('tebibytes', () => {
expect(getFormatter(SUPPORTED_FORMATS.tebibytes)(1)).toBe('1TiB');
expect(getFormatter(SUPPORTED_FORMATS.tebibytes)(1, 1)).toBe('1.0TiB');
});
it('tebibytes', () => {
expect(tebibytes(1)).toBe('1TiB');
expect(tebibytes(1, 1)).toBe('1.0TiB');
});
it('pebibytes', () => {
expect(getFormatter(SUPPORTED_FORMATS.pebibytes)(1)).toBe('1PiB');
expect(getFormatter(SUPPORTED_FORMATS.pebibytes)(1, 1)).toBe('1.0PiB');
});
it('pebibytes', () => {
expect(pebibytes(1)).toBe('1PiB');
expect(pebibytes(1, 1)).toBe('1.0PiB');
});
describe('when get formatter format is incorrect', () => {
it('formatter fails', () => {
expect(() => getFormatter('not-supported')(1)).toThrow();
describe('getFormatter', () => {
it.each([
[1],
[10],
[200],
[100],
[1000],
[10_000],
[100_000],
[1_000_000],
[10 ** 6],
[10 ** 9],
[0.1],
[0.5],
[0.888888],
])('formatting functions yield the same result as getFormatter for %d', (value) => {
expect(number(value)).toBe(getFormatter(SUPPORTED_FORMATS.number)(value));
expect(percent(value)).toBe(getFormatter(SUPPORTED_FORMATS.percent)(value));
expect(percentHundred(value)).toBe(getFormatter(SUPPORTED_FORMATS.percentHundred)(value));
expect(seconds(value)).toBe(getFormatter(SUPPORTED_FORMATS.seconds)(value));
expect(milliseconds(value)).toBe(getFormatter(SUPPORTED_FORMATS.milliseconds)(value));
expect(decimalBytes(value)).toBe(getFormatter(SUPPORTED_FORMATS.decimalBytes)(value));
expect(kilobytes(value)).toBe(getFormatter(SUPPORTED_FORMATS.kilobytes)(value));
expect(megabytes(value)).toBe(getFormatter(SUPPORTED_FORMATS.megabytes)(value));
expect(gigabytes(value)).toBe(getFormatter(SUPPORTED_FORMATS.gigabytes)(value));
expect(terabytes(value)).toBe(getFormatter(SUPPORTED_FORMATS.terabytes)(value));
expect(petabytes(value)).toBe(getFormatter(SUPPORTED_FORMATS.petabytes)(value));
expect(bytes(value)).toBe(getFormatter(SUPPORTED_FORMATS.bytes)(value));
expect(kibibytes(value)).toBe(getFormatter(SUPPORTED_FORMATS.kibibytes)(value));
expect(mebibytes(value)).toBe(getFormatter(SUPPORTED_FORMATS.mebibytes)(value));
expect(gibibytes(value)).toBe(getFormatter(SUPPORTED_FORMATS.gibibytes)(value));
expect(tebibytes(value)).toBe(getFormatter(SUPPORTED_FORMATS.tebibytes)(value));
expect(pebibytes(value)).toBe(getFormatter(SUPPORTED_FORMATS.pebibytes)(value));
expect(engineering(value)).toBe(getFormatter(SUPPORTED_FORMATS.engineering)(value));
});
describe('when get formatter format is incorrect', () => {
it('formatter fails', () => {
expect(() => getFormatter('not-supported')(1)).toThrow();
});
});
});
});
import { setLanguage } from 'helpers/locale_helper';
import { createDateTimeFormat, languageCode } from '~/locale';
import { createDateTimeFormat, formatNumber, languageCode } from '~/locale';
describe('locale', () => {
afterEach(() => setLanguage(null));
......@@ -27,4 +27,68 @@ describe('locale', () => {
expect(dateFormat.format(new Date(2015, 6, 3))).toBe('July 3, 2015');
});
});
describe('formatNumber', () => {
it('formats numbers', () => {
expect(formatNumber(1)).toBe('1');
expect(formatNumber(12345)).toBe('12,345');
});
it('formats bigint numbers', () => {
expect(formatNumber(123456789123456789n)).toBe('123,456,789,123,456,789');
});
it('formats numbers with options', () => {
expect(formatNumber(1, { style: 'percent' })).toBe('100%');
expect(formatNumber(1, { style: 'currency', currency: 'USD' })).toBe('$1.00');
});
it('formats localized numbers', () => {
expect(formatNumber(12345, {}, 'es')).toBe('12.345');
});
it('formats NaN', () => {
expect(formatNumber(NaN)).toBe('NaN');
});
it('formats infinity', () => {
expect(formatNumber(Number.POSITIVE_INFINITY)).toBe('');
});
it('formats negative infinity', () => {
expect(formatNumber(Number.NEGATIVE_INFINITY)).toBe('-∞');
});
it('formats EPSILON', () => {
expect(formatNumber(Number.EPSILON)).toBe('0');
});
describe('non-number values should pass through', () => {
it('undefined', () => {
expect(formatNumber(undefined)).toBe(undefined);
});
it('null', () => {
expect(formatNumber(null)).toBe(null);
});
it('arrays', () => {
expect(formatNumber([])).toEqual([]);
});
it('objects', () => {
expect(formatNumber({ a: 'b' })).toEqual({ a: 'b' });
});
});
describe('when in a different locale', () => {
beforeEach(() => {
setLanguage('es');
});
it('formats localized numbers', () => {
expect(formatNumber(12345)).toBe('12.345');
});
});
});
});
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment