mirror of
https://github.com/kingomarnajjar/flex-template-web.git
synced 2026-07-26 06:47:17 +10:00
CurrencyInput component and value formatting as strings
This commit is contained in:
parent
47db19acec
commit
0de0ff723d
6 changed files with 558 additions and 0 deletions
53
src/components/CurrencyInput/CurrencyInput.example.js
Normal file
53
src/components/CurrencyInput/CurrencyInput.example.js
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
/* eslint-disable import/prefer-default-export */
|
||||
import React, { PropTypes } from 'react';
|
||||
import { IntlProvider, addLocaleData } from 'react-intl';
|
||||
import en from 'react-intl/locale-data/en';
|
||||
import fi from 'react-intl/locale-data/fi';
|
||||
import { currencyDefaultConfig } from '../../util/currency';
|
||||
import CurrencyInput from './CurrencyInput';
|
||||
|
||||
const defaultConfig = {
|
||||
...currencyDefaultConfig,
|
||||
currency: 'USD',
|
||||
};
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
const onChange = price => console.log('CurrencyInput - value:', price);
|
||||
|
||||
// Different locales need to be initialized before their currency formatting is in use
|
||||
const CurrencyInputWithIntl = ({ locale, ...rest }) => {
|
||||
if (locale === 'en-US') {
|
||||
addLocaleData([...en]);
|
||||
} else {
|
||||
addLocaleData([...fi]);
|
||||
}
|
||||
return (
|
||||
<IntlProvider locale={locale}><CurrencyInput {...rest} input={{ onChange }} /></IntlProvider>
|
||||
);
|
||||
};
|
||||
|
||||
const { object, string } = PropTypes;
|
||||
|
||||
CurrencyInputWithIntl.propTypes = {
|
||||
currencyConfig: object.isRequired,
|
||||
locale: string.isRequired,
|
||||
};
|
||||
|
||||
// Empty field with en-US locale
|
||||
export const EmptyWithEnUS = {
|
||||
component: CurrencyInputWithIntl,
|
||||
props: {
|
||||
currencyConfig: defaultConfig,
|
||||
locale: 'en-US',
|
||||
},
|
||||
};
|
||||
|
||||
// Default value with fi-FI locale
|
||||
export const defaultValueWithFiFI = {
|
||||
component: CurrencyInputWithIntl,
|
||||
props: {
|
||||
currencyConfig: { ...defaultConfig, currency: 'EUR' },
|
||||
locale: 'fi-FI',
|
||||
defaultValue: 9999.99,
|
||||
},
|
||||
};
|
||||
210
src/components/CurrencyInput/CurrencyInput.js
Normal file
210
src/components/CurrencyInput/CurrencyInput.js
Normal file
|
|
@ -0,0 +1,210 @@
|
|||
/**
|
||||
* CurrencyInput renders an input field that format it's value according to currency formatting rules
|
||||
* onFocus: renders given value in unformatted manner: "9999,99"
|
||||
* onBlur: formats the given input: "9 999,99 €"
|
||||
*/
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
import { intlShape, injectIntl } from 'react-intl';
|
||||
import { types } from 'sharetribe-sdk';
|
||||
import {
|
||||
convertUnitToSubUnit,
|
||||
currencyDefaultConfig,
|
||||
ensureDotSeparator,
|
||||
ensureSeparator,
|
||||
truncateToSubUnitPrecision,
|
||||
} from '../../util/currency';
|
||||
|
||||
const allowedInputProps = allProps => {
|
||||
// Strip away props that are not passed to input element (or are overwritten)
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const { currencyConfig, defaultValue, intl, input, meta, ...inputProps } = allProps;
|
||||
return inputProps || {};
|
||||
};
|
||||
|
||||
// Convert unformatted value (e.g. 10,00) to Money (or null)
|
||||
const getPrice = (unformattedValue, currency) => {
|
||||
const isEmptyString = unformattedValue === '';
|
||||
return isEmptyString
|
||||
? null
|
||||
: new types.Money(convertUnitToSubUnit(unformattedValue, currency), currency);
|
||||
};
|
||||
|
||||
class CurrencyInput extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
const { currencyConfig, defaultValue, intl } = props;
|
||||
|
||||
// We need to handle number format - some locales use dots and some points as decimal separator
|
||||
// TODO Figure out if this could be digged from React-Intl directly somehow
|
||||
const testSubUnitFormat = intl.formatNumber('1.1', currencyConfig);
|
||||
const usesPoint = testSubUnitFormat.indexOf(',') >= 0;
|
||||
|
||||
try {
|
||||
// whatever is passed as a default value, will be converted to currency string
|
||||
// Unformatted value is digits + localized sub unit separator ("9,99")
|
||||
const unformattedValue = defaultValue
|
||||
? truncateToSubUnitPrecision(
|
||||
ensureSeparator(defaultValue.toString(), usesPoint),
|
||||
currencyConfig.currency,
|
||||
usesPoint
|
||||
)
|
||||
: '';
|
||||
// Formatted value fully localized currency string ("$1,000.99")
|
||||
const formattedValue = defaultValue
|
||||
? intl.formatNumber(ensureDotSeparator(unformattedValue), currencyConfig)
|
||||
: '';
|
||||
|
||||
this.state = {
|
||||
formattedValue,
|
||||
unformattedValue,
|
||||
value: formattedValue,
|
||||
usesPoint,
|
||||
};
|
||||
} catch (e) {
|
||||
// Print error, if default value isn't supported (see specs: truncateToSubUnitPrecision).
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('CurrencyInput:', e);
|
||||
throw e;
|
||||
}
|
||||
|
||||
this.onInputChange = this.onInputChange.bind(this);
|
||||
this.onInputBlur = this.onInputBlur.bind(this);
|
||||
this.onInputFocus = this.onInputFocus.bind(this);
|
||||
this.updateValues = this.updateValues.bind(this);
|
||||
}
|
||||
|
||||
shouldComponentUpdate(nextProps, nextState) {
|
||||
return !(nextProps === this.props && nextState === this.state);
|
||||
}
|
||||
|
||||
onInputChange(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
// Update value strings on state
|
||||
const { unformattedValue } = this.updateValues(event);
|
||||
// Notify parent component about current price change
|
||||
const price = getPrice(
|
||||
ensureDotSeparator(unformattedValue),
|
||||
this.props.currencyConfig.currency
|
||||
);
|
||||
this.props.input.onChange(price);
|
||||
}
|
||||
|
||||
onInputBlur(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
const onBlur = this.props.input.onBlur;
|
||||
const currency = this.props.currencyConfig.currency;
|
||||
this.setState(prevState => {
|
||||
if (onBlur) {
|
||||
// If parent component has provided onBlur function, call it with current price.
|
||||
const price = getPrice(ensureDotSeparator(prevState.unformattedValue), currency);
|
||||
onBlur(price);
|
||||
}
|
||||
return {
|
||||
value: prevState.formattedValue,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
onInputFocus(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
const onFocus = this.props.input.onFocus;
|
||||
const currency = this.props.currencyConfig.currency;
|
||||
this.setState(prevState => {
|
||||
if (onFocus) {
|
||||
// If parent component has provided onFocus function, call it with current price.
|
||||
const price = getPrice(ensureDotSeparator(prevState.unformattedValue), currency);
|
||||
onFocus(price);
|
||||
}
|
||||
return {
|
||||
value: prevState.unformattedValue,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
updateValues(event) {
|
||||
try {
|
||||
const { currencyConfig, intl } = this.props;
|
||||
const targetValue = event.target.value;
|
||||
const isEmptyString = targetValue === '';
|
||||
const valueOrZero = isEmptyString ? '0' : targetValue;
|
||||
|
||||
// truncate decimals to subunit precision: 10000.999 => 10000.99
|
||||
const truncatedValueString = truncateToSubUnitPrecision(
|
||||
valueOrZero,
|
||||
currencyConfig.currency,
|
||||
this.state.usesPoint
|
||||
);
|
||||
const unformattedValue = !isEmptyString ? truncatedValueString : '';
|
||||
const formattedValue = !isEmptyString
|
||||
? intl.formatNumber(ensureDotSeparator(truncatedValueString), currencyConfig)
|
||||
: '';
|
||||
|
||||
this.setState({
|
||||
formattedValue,
|
||||
value: unformattedValue,
|
||||
unformattedValue,
|
||||
});
|
||||
|
||||
return { formattedValue, value: unformattedValue, unformattedValue };
|
||||
} catch (e) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(e);
|
||||
|
||||
// If an error occurs while filling input field, use previous values
|
||||
// This ensures that string like '12.3r' doesn't end up to a state.
|
||||
const { formattedValue, unformattedValue, value } = this.state;
|
||||
return { formattedValue, unformattedValue, value };
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { currencyConfig, defaultValue, placeholder, intl } = this.props;
|
||||
const placeholderText = placeholder || intl.formatNumber(defaultValue, currencyConfig);
|
||||
return (
|
||||
<input
|
||||
{...allowedInputProps(this.props)}
|
||||
value={this.state.value}
|
||||
onChange={this.onInputChange}
|
||||
onBlur={this.onInputBlur}
|
||||
onFocus={this.onInputFocus}
|
||||
type="text"
|
||||
placeholder={placeholderText}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
CurrencyInput.defaultProps = {
|
||||
currencyConfig: currencyDefaultConfig,
|
||||
defaultValue: 0,
|
||||
input: null,
|
||||
placeholder: null,
|
||||
};
|
||||
|
||||
const { bool, func, instanceOf, oneOfType, number, shape, string } = PropTypes;
|
||||
|
||||
CurrencyInput.propTypes = {
|
||||
currencyConfig: shape({
|
||||
style: string.isRequired,
|
||||
currency: string.isRequired,
|
||||
currencyDisplay: string,
|
||||
useGrouping: bool,
|
||||
minimumFractionDigits: number,
|
||||
maximumFractionDigits: number,
|
||||
}).isRequired,
|
||||
defaultValue: number,
|
||||
intl: intlShape.isRequired,
|
||||
input: shape({
|
||||
value: oneOfType([string, instanceOf(types.Money)]),
|
||||
onBlur: func,
|
||||
onChange: func.isRequired,
|
||||
onFocus: func,
|
||||
}).isRequired,
|
||||
|
||||
placeholder: string,
|
||||
};
|
||||
|
||||
export default injectIntl(CurrencyInput);
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
import AddImages from './AddImages/AddImages';
|
||||
import BookingInfo from './BookingInfo/BookingInfo';
|
||||
import CurrencyInput from './CurrencyInput/CurrencyInput';
|
||||
import Discussion from './Discussion/Discussion';
|
||||
import FilterPanel from './FilterPanel/FilterPanel';
|
||||
import HeroSection from './HeroSection/HeroSection';
|
||||
|
|
@ -22,6 +23,7 @@ import SearchResultsPanel from './SearchResultsPanel/SearchResultsPanel';
|
|||
export {
|
||||
AddImages,
|
||||
BookingInfo,
|
||||
CurrencyInput,
|
||||
Discussion,
|
||||
FilterPanel,
|
||||
HeroSection,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
// components
|
||||
import * as AddImages from './components/AddImages/AddImages.example';
|
||||
import * as BookingInfo from './components/BookingInfo/BookingInfo.example';
|
||||
import * as CurrencyInput from './components/CurrencyInput/CurrencyInput.example';
|
||||
import * as ListingCard from './components/ListingCard/ListingCard.example';
|
||||
import * as Map from './components/Map/Map.example';
|
||||
import * as NamedLink from './components/NamedLink/NamedLink.example';
|
||||
|
|
@ -23,6 +24,7 @@ export {
|
|||
BookingInfo,
|
||||
ChangeAccountPasswordForm,
|
||||
ChangePasswordForm,
|
||||
CurrencyInput,
|
||||
EditListingForm,
|
||||
HeroSearchForm,
|
||||
ListingCard,
|
||||
|
|
|
|||
106
src/util/currency.js
Normal file
106
src/util/currency.js
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
import { trimEnd } from 'lodash';
|
||||
import Decimal from 'decimal.js';
|
||||
|
||||
// This works for USD and EUR
|
||||
const SUB_UNITS_DIVISOR = 100;
|
||||
|
||||
// Default config for currency formatting (for React-Intl).
|
||||
export const currencyDefaultConfig = {
|
||||
style: 'currency',
|
||||
currency: 'USD',
|
||||
currencyDisplay: 'symbol',
|
||||
useGrouping: true,
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
};
|
||||
|
||||
// Ensures that the given string uses only dots or points
|
||||
// ensureSeparator('9999999,99', false) // => '9999999.99'
|
||||
export const ensureSeparator = (str, usePoint = false) => {
|
||||
if (typeof str !== 'string') {
|
||||
throw new Error('Parameter must be a string');
|
||||
}
|
||||
return usePoint ? str.split('.').join(',') : str.split(',').join('.');
|
||||
};
|
||||
|
||||
// Ensures that the given string uses only dots (e.g. JavaScript floats use dots)
|
||||
export const ensureDotSeparator = str => {
|
||||
return ensureSeparator(str, false);
|
||||
};
|
||||
|
||||
// Convert string to Decimal object (from Decimal.js math library)
|
||||
export const convertToDecimal = str => {
|
||||
const dotFormattedStr = ensureDotSeparator(str);
|
||||
try {
|
||||
return new Decimal(dotFormattedStr);
|
||||
} catch (e) {
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
|
||||
// Converts Decimal object to string
|
||||
export const convertDecimalToString = (decimalValue, usePoint = false) => {
|
||||
try {
|
||||
const d = new Decimal(decimalValue);
|
||||
return ensureSeparator(d.toString(), usePoint);
|
||||
} catch (e) {
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
|
||||
// Limits value to sub-unit precision: "1.4567" -> "1.45"
|
||||
// Useful in input fields so this doesn't use rounding.
|
||||
export const truncateToSubUnitPrecision = (inputString, currency, usePoint = false) => {
|
||||
if (!(currency === 'USD' || currency === 'EUR')) {
|
||||
// Other currencies sub unit support need to be changed case by case
|
||||
throw new Error('Currency must be either USD or EUR');
|
||||
}
|
||||
|
||||
// '10,' should be passed through, but that format is not supported as valid number
|
||||
const trimmed = trimEnd(inputString, usePoint ? ',' : '.');
|
||||
// create another instance and check if value is convertable
|
||||
const value = convertToDecimal(trimmed, usePoint);
|
||||
|
||||
if (value.isNegative()) {
|
||||
throw new Error('Parameter must be a positive number');
|
||||
}
|
||||
|
||||
// Amount is always counted in subunits
|
||||
// E.g. $10 => 1000¢
|
||||
const amount = value.times(SUB_UNITS_DIVISOR);
|
||||
|
||||
// Amount must be integer
|
||||
// We don't deal with subunit fragments like 1000.345¢
|
||||
if (amount.isInteger()) {
|
||||
// accepted strings: '9', '9,' '9.' '9,99'
|
||||
const decimalCount2 = value.toFixed(2);
|
||||
const decimalPrecisionMax2 = decimalCount2.length >= inputString.length
|
||||
? inputString
|
||||
: value.toFixed(2);
|
||||
return ensureSeparator(decimalPrecisionMax2, usePoint);
|
||||
} else {
|
||||
// truncate strings ('9.999' => '9.99')
|
||||
const truncated = amount.truncated().dividedBy(SUB_UNITS_DIVISOR);
|
||||
return convertDecimalToString(truncated, usePoint);
|
||||
}
|
||||
};
|
||||
|
||||
// Converts given value to sub unit value and returns it as a number
|
||||
export const convertUnitToSubUnit = (value, currency, usePoint = false) => {
|
||||
if (!(currency === 'USD' || currency === 'EUR')) {
|
||||
throw new Error('Currency must be either USD or EUR');
|
||||
}
|
||||
|
||||
if (!(typeof value === 'string' || typeof value === 'number')) {
|
||||
throw new Error('Value must be either number or string');
|
||||
}
|
||||
|
||||
const val = typeof value === 'string' ? convertToDecimal(value, usePoint) : new Decimal(value);
|
||||
const amount = val.times(SUB_UNITS_DIVISOR);
|
||||
|
||||
if (amount.isInteger()) {
|
||||
return amount.toNumber();
|
||||
} else {
|
||||
throw new Error(`value must divisible by ${SUB_UNITS_DIVISOR}`);
|
||||
}
|
||||
};
|
||||
185
src/util/currency.test.js
Normal file
185
src/util/currency.test.js
Normal file
|
|
@ -0,0 +1,185 @@
|
|||
import Decimal from 'decimal.js';
|
||||
import {
|
||||
convertDecimalToString,
|
||||
convertToDecimal,
|
||||
convertUnitToSubUnit,
|
||||
ensureSeparator,
|
||||
truncateToSubUnitPrecision,
|
||||
} from './currency';
|
||||
|
||||
describe('currency utils', () => {
|
||||
describe('ensureSeparator(str)', () => {
|
||||
it('changes points in string to dots ', () => {
|
||||
expect(ensureSeparator('0')).toEqual('0');
|
||||
expect(ensureSeparator('0.0')).toEqual('0.0');
|
||||
expect(ensureSeparator('0.')).toEqual('0.');
|
||||
expect(ensureSeparator('0,')).toEqual('0.');
|
||||
expect(ensureSeparator('0,0')).toEqual('0.0');
|
||||
expect(ensureSeparator('0,1234')).toEqual('0.1234');
|
||||
expect(ensureSeparator('asdf')).toEqual('asdf');
|
||||
});
|
||||
|
||||
it('changes dots in string to points ', () => {
|
||||
expect(ensureSeparator('0', true)).toEqual('0');
|
||||
expect(ensureSeparator('0.0', true)).toEqual('0,0');
|
||||
expect(ensureSeparator('0.', true)).toEqual('0,');
|
||||
expect(ensureSeparator('0,', true)).toEqual('0,');
|
||||
expect(ensureSeparator('0,0', true)).toEqual('0,0');
|
||||
expect(ensureSeparator('0,1234', true)).toEqual('0,1234');
|
||||
expect(ensureSeparator('asdf', true)).toEqual('asdf');
|
||||
});
|
||||
|
||||
it('Throws exceptions if parameter is not a string', () => {
|
||||
expect(() => ensureSeparator(0)).toThrowError('Parameter must be a string');
|
||||
expect(() => ensureSeparator(true)).toThrowError('Parameter must be a string');
|
||||
expect(() => ensureSeparator([])).toThrowError('Parameter must be a string');
|
||||
expect(() => ensureSeparator({})).toThrowError('Parameter must be a string');
|
||||
expect(() => ensureSeparator(0, true)).toThrowError('Parameter must be a string');
|
||||
expect(() => ensureSeparator(true, true)).toThrowError('Parameter must be a string');
|
||||
expect(() => ensureSeparator([], true)).toThrowError('Parameter must be a string');
|
||||
expect(() => ensureSeparator({}, true)).toThrowError('Parameter must be a string');
|
||||
});
|
||||
});
|
||||
|
||||
describe('convertToDecimal(valueStr)', () => {
|
||||
it('converts numbers in string format to Decimal', () => {
|
||||
expect(convertToDecimal('0')).toEqual(new Decimal('0'));
|
||||
expect(convertToDecimal('-0')).toEqual(new Decimal('-0'));
|
||||
expect(convertToDecimal('1.23')).toEqual(new Decimal('1.23'));
|
||||
expect(convertToDecimal('999999999.99999999')).toEqual(new Decimal('999999999.99999999'));
|
||||
expect(convertToDecimal('-999999999.99999999')).toEqual(new Decimal('-999999999.99999999'));
|
||||
});
|
||||
|
||||
it('Throws exceptions if string formatted parameter can not be converted to Decimal', () => {
|
||||
expect(() => convertToDecimal('asdf')).toThrowError('[DecimalError] Invalid argument: asdf');
|
||||
expect(() => convertToDecimal('123asdf')).toThrowError(
|
||||
'[DecimalError] Invalid argument: 123asdf'
|
||||
);
|
||||
});
|
||||
|
||||
it('Throws exceptions if parameter is not a string', () => {
|
||||
expect(() => convertToDecimal(true)).toThrowError('Parameter must be a string');
|
||||
expect(() => convertToDecimal([])).toThrowError('Parameter must be a string');
|
||||
expect(() => convertToDecimal(undefined)).toThrowError('Parameter must be a string');
|
||||
expect(() => convertToDecimal(null)).toThrowError('Parameter must be a string');
|
||||
});
|
||||
});
|
||||
|
||||
describe('convertDecimalToString(<Decimal>)', () => {
|
||||
it('converts Decimals to string', () => {
|
||||
expect(convertDecimalToString(new Decimal('0'))).toEqual('0');
|
||||
expect(convertDecimalToString(new Decimal('-0'))).toEqual('0');
|
||||
expect(convertDecimalToString(new Decimal('1.23'))).toEqual('1.23');
|
||||
expect(convertDecimalToString(new Decimal('999999999.99999999'))).toEqual(
|
||||
'999999999.99999999'
|
||||
);
|
||||
expect(convertDecimalToString(new Decimal('-999999999.99999999'))).toEqual(
|
||||
'-999999999.99999999'
|
||||
);
|
||||
});
|
||||
|
||||
it('converts numbers in string format to string', () => {
|
||||
expect(convertDecimalToString('0')).toEqual('0');
|
||||
expect(convertDecimalToString('-0')).toEqual('0');
|
||||
expect(convertDecimalToString('1.23')).toEqual('1.23');
|
||||
expect(convertDecimalToString('999999999.99999999')).toEqual('999999999.99999999');
|
||||
expect(convertDecimalToString('-999999999.99999999')).toEqual('-999999999.99999999');
|
||||
});
|
||||
|
||||
it('Throws exceptions if string formatted parameter is not a number', () => {
|
||||
expect(() => convertDecimalToString('asdf')).toThrowError(
|
||||
'[DecimalError] Invalid argument: asdf'
|
||||
);
|
||||
expect(() => convertDecimalToString('123asdf')).toThrowError(
|
||||
'[DecimalError] Invalid argument: 123asdf'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('truncateToSubUnitPrecision(valueStr, currency)', () => {
|
||||
it('positive values', () => {
|
||||
expect(truncateToSubUnitPrecision('0', 'USD')).toEqual('0');
|
||||
expect(truncateToSubUnitPrecision('1', 'EUR')).toEqual('1');
|
||||
expect(truncateToSubUnitPrecision('10', 'USD')).toEqual('10');
|
||||
expect(truncateToSubUnitPrecision('10000', 'EUR')).toEqual('10000');
|
||||
expect(truncateToSubUnitPrecision('10000.00', 'USD')).toEqual('10000.00');
|
||||
expect(truncateToSubUnitPrecision('99.9', 'EUR')).toEqual('99.9');
|
||||
expect(truncateToSubUnitPrecision('99.99', 'USD')).toEqual('99.99');
|
||||
});
|
||||
|
||||
it('truncate excess amount of decimal values', () => {
|
||||
expect(truncateToSubUnitPrecision('99.999999', 'USD')).toEqual('99.99');
|
||||
expect(truncateToSubUnitPrecision('1.111', 'USD')).toEqual('1.11');
|
||||
expect(truncateToSubUnitPrecision('1.110000', 'USD')).toEqual('1.11');
|
||||
});
|
||||
|
||||
it('negative values to throw errors', () => {
|
||||
expect(() => truncateToSubUnitPrecision('-0', 'USD')).toThrowError(
|
||||
'Parameter must be a positive number'
|
||||
);
|
||||
expect(() => truncateToSubUnitPrecision('-1', 'USD')).toThrowError(
|
||||
'Parameter must be a positive number'
|
||||
);
|
||||
expect(() => truncateToSubUnitPrecision('-10000', 'USD')).toThrowError(
|
||||
'Parameter must be a positive number'
|
||||
);
|
||||
expect(() => truncateToSubUnitPrecision('-99.99', 'USD')).toThrowError(
|
||||
'Parameter must be a positive number'
|
||||
);
|
||||
expect(() => truncateToSubUnitPrecision('-99.99999', 'USD')).toThrowError(
|
||||
'Parameter must be a positive number'
|
||||
);
|
||||
expect(() => truncateToSubUnitPrecision('-1.111', 'USD')).toThrowError(
|
||||
'Parameter must be a positive number'
|
||||
);
|
||||
});
|
||||
|
||||
it('text input to throw errors', () => {
|
||||
expect(() => truncateToSubUnitPrecision('asdf', 'USD')).toThrowError();
|
||||
expect(() => truncateToSubUnitPrecision('100asdf', 'USD')).toThrowError();
|
||||
expect(() => truncateToSubUnitPrecision('asdf100', 'USD')).toThrowError();
|
||||
expect(() => truncateToSubUnitPrecision('@', 'USD')).toThrowError();
|
||||
});
|
||||
|
||||
it('wrong currency to throw errors', () => {
|
||||
expect(() => truncateToSubUnitPrecision('asdf', 'JPN')).toThrowError();
|
||||
expect(() => truncateToSubUnitPrecision('100asdf', 'JPN')).toThrowError();
|
||||
expect(() => truncateToSubUnitPrecision('asdf100', 'JPN')).toThrowError();
|
||||
expect(() => truncateToSubUnitPrecision('@', 'JPN')).toThrowError();
|
||||
});
|
||||
});
|
||||
|
||||
describe('convertUnitToSubUnit(value, currency)', () => {
|
||||
it('numbers as value', () => {
|
||||
expect(convertUnitToSubUnit(0, 'USD')).toEqual(0);
|
||||
expect(convertUnitToSubUnit(10, 'USD')).toEqual(1000);
|
||||
expect(convertUnitToSubUnit(1, 'EUR')).toEqual(100);
|
||||
});
|
||||
|
||||
it('strings as value', () => {
|
||||
expect(convertUnitToSubUnit('0', 'USD')).toEqual(0);
|
||||
expect(convertUnitToSubUnit('10', 'USD')).toEqual(1000);
|
||||
expect(convertUnitToSubUnit('1', 'EUR')).toEqual(100);
|
||||
expect(convertUnitToSubUnit('0.10', 'USD')).toEqual(10);
|
||||
expect(convertUnitToSubUnit('10,99', 'USD')).toEqual(1099);
|
||||
});
|
||||
|
||||
it('wrong type', () => {
|
||||
expect(() => convertUnitToSubUnit({}, 'USD')).toThrowError(
|
||||
'Value must be either number or string'
|
||||
);
|
||||
expect(() => convertUnitToSubUnit([], 'USD')).toThrowError(
|
||||
'Value must be either number or string'
|
||||
);
|
||||
expect(() => convertUnitToSubUnit(null, 'EUR')).toThrowError(
|
||||
'Value must be either number or string'
|
||||
);
|
||||
});
|
||||
|
||||
it('wrong currency', () => {
|
||||
expect(() => convertUnitToSubUnit(1, 'JPN')).toThrowError(
|
||||
'Currency must be either USD or EUR'
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Reference in a new issue