// License: LGPL-3.0-or-later import BigNumber from 'bignumber.js'; import { Money, MoneyAsJson, Operand, RoundingMode } from './money'; describe("Money", () => { describe('Money.fromCents', () => { it('succeeds from a old Money object', () => { expect.assertions(2); const old = Money.fromCents(333, 'eur'); const result = Money.fromCents(old); expect(result).toStrictEqual(old); expect(result).not.toBe(old); }); it('succeeds from a json', () => { expect.hasAssertions(); const old:MoneyAsJson = { cents: 333, currency: 'eur' }; const result = Money.fromCents(old); // eslint-disable-next-line jest/prefer-strict-equal expect(result).toEqual(old); expect(result).toBeInstanceOf(Money); }); it('succeeds from a stringy json', () => { expect.hasAssertions(); const old = Money.fromCents(333, 'eur'); const result = Money.fromCents({ cents: '333', currency: 'eur' }); // eslint-disable-next-line jest/prefer-strict-equal expect(result).toEqual(old); expect(result).toBeInstanceOf(Money); }); it('succeeds from function parameters', () => { expect.hasAssertions(); const result = Money.fromCents(333, 'eur'); // eslint-disable-next-line jest/prefer-strict-equal expect(result).toEqual({ cents: 333, currency: 'eur' }); expect(result).toBeInstanceOf(Money); }); it('succeeds from BigNumber', () => { expect.hasAssertions(); const result = Money.fromCents(new BigNumber(333), 'eur'); // eslint-disable-next-line jest/prefer-strict-equal expect(result).toEqual({ cents: 333, currency: 'eur' }); }); it('rejects if string is not an integer', () => { expect.hasAssertions(); expect(() => { Money.fromCents('3344.4', 'usd'); }).toThrow(TypeError); }); it('rejects if BigNumber is not an integer', () => { expect.hasAssertions(); expect(() => { Money.fromCents(new BigNumber('3344.4'), 'usd'); }).toThrow(TypeError); }); it('rejects if number is not an integer', () => { expect.hasAssertions(); expect(() => { Money.fromCents(444.333, 'usd'); }).toThrow(TypeError); }); }); const cents1000 = Money.fromCents(1000, 'usd'); const cents500 = Money.fromCents(500, 'usd'); const euCents1000 = Money.fromCents(1000, 'eur'); const negative1000 = Money.fromCents(-1000, 'usd'); function verifyCurrency(func:(m:Money, other:Money) => unknown){ expect.assertions(1); expect(() => func(cents1000, euCents1000)).toThrow(TypeError); } describe.each([ ['add', (m:Money, other:Money) => m.add(other)], ['compare', (m:Money, other:Money) => m.compare(other)], ['divide', (m:Money, other:Money) => m.divide(other)], ['greaterThan', (m:Money, other:Money) => m.greaterThan(other)], ['greaterThanOrEqual', (m:Money, other:Money) => m.greaterThanOrEqual(other)], ['lessThan', (m:Money, other:Money) => m.lessThan(other)], ['lessThanOrEqual', (m:Money, other:Money) => m.lessThanOrEqual(other)], ['multiply', (m:Money, other:Money) => m.multiply(other)], ['subtract', (m:Money, other:Money) => m.subtract(other)], ])('.%s with %s', (_name, func) => { it(`throw when currency doesn't match`, () => { expect.assertions(1); expect(() => func(cents1000, euCents1000)).toThrow(TypeError); }); }); describe.each([ ['add', (m:Money, other:Operand) => m.add(other)], ['subtract', (m:Money, other:Operand) => m.subtract(other)], ])('.%s with %s', (_name, func) => { it(`throw when other is a decimal number`, () => { expect.assertions(1); expect(() => func(cents1000, 4.5)).toThrow(TypeError); }); it(`throw when other is a decimal BigNumber`, () => { expect.assertions(1); expect(() => func(cents1000, new BigNumber('4.5'))).toThrow(TypeError); }); }); it('.isZero', () => { expect.assertions(1); expect(cents1000.isZero()).toStrictEqual(false); }); it('.isPositive', () => { expect.assertions(1); expect(cents1000.isPositive()).toStrictEqual(true); }); it('.isNegative', () => { expect.assertions(1); expect(negative1000.isNegative()).toStrictEqual(true); }); describe('.compare', () => { describe('greater', () => { describe('Money', () => { it('same currency', () => { expect.assertions(1); expect(cents1000.compare(cents500)).toStrictEqual(1); }); it('different currency', () => { expect.assertions(1); expect(() => { cents1000.compare(euCents1000); }).toThrow(TypeError); }); }); describe('BigNumber', () => { it('same currency', () => { expect.assertions(1); expect(cents1000.compare(cents500)).toStrictEqual(1); }); it('different currency', () => { expect.assertions(1); expect(() => { cents1000.compare(euCents1000); }).toThrow(TypeError); }); }); }); }); describe('.divide', () => { it('divides 36 into 9', () => { expect.assertions(1); expect(Money.fromCents(36, 'usd').divide(9).toJSON()).toStrictEqual({cents: 4, currency: 'usd'}); }); it('throws if the currencies do not match', () => { expect.assertions(1); verifyCurrency((m, other) => m.divide(other)); }); it('defaults to rounding to HalfUp', () => { expect.assertions(3); expect(Money.fromCents(40, 'usd').divide(Money.fromCents(9, 'usd')).toJSON()).toStrictEqual({cents: 4, currency: 'usd'}); expect(Money.fromCents(41, 'usd').divide(Money.fromCents(9, 'usd')).toJSON()).toStrictEqual({cents: 5, currency: 'usd'}); expect(Money.fromCents(7, 'usd').divide(Money.fromCents(2, 'usd')).toJSON()).toStrictEqual({cents: 4, currency: 'usd'}); }); it('rounds to floor if requested', () => { expect.assertions(1); expect(Money.fromCents(41, 'usd').divide(Money.fromCents(9, 'usd'), RoundingMode.Floor).toJSON()).toStrictEqual({cents: 4, currency: 'usd'}); }); it('rounds to ceil if requested', () => { expect.assertions(1); expect(Money.fromCents(40, 'usd').divide(Money.fromCents(9, 'usd'), RoundingMode.Ceil).toJSON()).toStrictEqual({cents: 5, currency: 'usd'}); }); }); describe('.multiply', () => { it('multiply 9 x 4', () => { expect.assertions(1); expect(Money.fromCents(9, 'usd').multiply(4).toJSON()).toStrictEqual({cents: 36, currency: 'usd'}); }); it('throws if the currencies do not match', () => { expect.assertions(1); verifyCurrency((m, other) => m.multiply(other)); }); it('handles multiplying by a decimal properly', () => { expect.assertions(1); expect(Money.fromCents('3', 'usd').multiply(new BigNumber('1.263')).toJSON()).toStrictEqual({cents: 4, currency: 'usd'}); }); it('defaults to rounding to HalfUp', () => { expect.assertions(1); expect(Money.fromCents(7, 'usd').multiply(new BigNumber('.5')).toJSON()).toStrictEqual({cents: 4, currency: 'usd'}); }); it('rounds to floor if requested', () => { expect.assertions(1); expect(Money.fromCents('3', 'usd').multiply('1.263', RoundingMode.Floor).toJSON()).toStrictEqual({cents: 3, currency: 'usd'}); }); it('rounds to ceil if requested', () => { expect.assertions(1); expect(Money.fromCents('3', 'usd').multiply('1.263', RoundingMode.Ceil).toJSON()).toStrictEqual({cents: 4, currency: 'usd'}); }); }); describe('.toBigNumber', () => { it('doesnt round to nearest integer', () => { expect.assertions(1); expect(cents1000.toBigNumber().plus('1.2').toString()).toStrictEqual('1001.2'); }); }); });