Enforce ts tab indents for accessibility

This commit is contained in:
Eric 2020-06-26 19:35:09 -05:00 committed by Eric Schultz
parent e6dda7b88b
commit 48f2303216
3 changed files with 231 additions and 230 deletions

View file

@ -43,6 +43,7 @@ module.exports = {
"error", "error",
"always" "always"
], ],
"no-trailing-spaces": ["error"] "no-trailing-spaces": ["error"],
"indent": ["error", "tab"], // we use tabs for accessibility
} }
}; };

View file

@ -1,34 +1,34 @@
import { Money } from './money'; import { Money } from './money';
describe("Money", () => { describe("Money", () => {
describe('Money.fromCents', () => { describe('Money.fromCents', () => {
it('succeeds from a old Money object', () => { it('succeeds from a old Money object', () => {
expect.assertions(2); expect.assertions(2);
const old = Money.fromCents(333, 'eur'); const old = Money.fromCents(333, 'eur');
const result = Money.fromCents(old); const result = Money.fromCents(old);
expect(result).toStrictEqual(old); expect(result).toStrictEqual(old);
expect(result).not.toBe(old); expect(result).not.toBe(old);
}); });
it('succeeds from a json', () => { it('succeeds from a json', () => {
expect.hasAssertions(); expect.hasAssertions();
const old = { amount: 333, currency: 'eur' }; const old = { amount: 333, currency: 'eur' };
const result = Money.fromCents(old); const result = Money.fromCents(old);
// eslint-disable-next-line jest/prefer-strict-equal // eslint-disable-next-line jest/prefer-strict-equal
expect(result).toEqual(old); expect(result).toEqual(old);
expect(result).toBeInstanceOf(Money); expect(result).toBeInstanceOf(Money);
}); });
it('succeeds from function parameters', () => { it('succeeds from function parameters', () => {
expect.hasAssertions(); expect.hasAssertions();
const result = Money.fromCents(333, 'eur'); const result = Money.fromCents(333, 'eur');
// eslint-disable-next-line jest/prefer-strict-equal // eslint-disable-next-line jest/prefer-strict-equal
expect(result).toEqual({ amount: 333, currency: 'eur' }); expect(result).toEqual({ amount: 333, currency: 'eur' });
expect(result).toBeInstanceOf(Money); expect(result).toBeInstanceOf(Money);
}); });
}); });
}); });

View file

@ -3,269 +3,269 @@
import isFunction from 'lodash/isFunction'; import isFunction from 'lodash/isFunction';
const assertSameCurrency = function (left: Money, right: Money) { const assertSameCurrency = function (left: Money, right: Money) {
if (left.currency !== right.currency) if (left.currency !== right.currency)
throw new Error('Different currencies'); throw new Error('Different currencies');
}; };
const assertType = function (other: unknown) { const assertType = function (other: unknown) {
if (!(other instanceof Money)) if (!(other instanceof Money))
throw new TypeError('Instance of Money required'); throw new TypeError('Instance of Money required');
}; };
const assertOperand = function (operand: unknown) { const assertOperand = function (operand: unknown) {
if (typeof operand !== 'number' || isNaN(operand) && !isFinite(operand)) if (typeof operand !== 'number' || isNaN(operand) && !isFinite(operand))
throw new TypeError('Operand must be a number'); throw new TypeError('Operand must be a number');
}; };
type MoneyAsJson = {amount: number, currency: string} type MoneyAsJson = { amount: number, currency: string }
/** /**
* Represents a monetary amount. For safety, all Money objects are immutable. All of the functions in this class create a new Money object. * Represents a monetary amount. For safety, all Money objects are immutable. All of the functions in this class create a new Money object.
* *
* To create a new Money object is to use the `fromCents` function. * To create a new Money object is to use the `fromCents` function.
* @export * @export
* @class Money * @class Money
*/ */
export class Money { export class Money {
readonly currency:string readonly currency: string
protected constructor(readonly amount: number, currency: string) { protected constructor(readonly amount: number, currency: string) {
this.currency = currency.toLowerCase(); this.currency = currency.toLowerCase();
const methodsToBind = [this.equals, this.add, this.subtract, this.multiply, this.divide, this.allocate, const methodsToBind = [this.equals, this.add, this.subtract, this.multiply, this.divide, this.allocate,
this.compare, this.greaterThan, this.greaterThanOrEqual, this.lessThan, this.compare, this.greaterThan, this.greaterThanOrEqual, this.lessThan,
this.lessThanOrEqual, this.isZero, this.isPositive, this.isNegative, this.lessThanOrEqual, this.isZero, this.isPositive, this.isNegative,
this.toJSON]; this.toJSON];
methodsToBind.forEach((func) => Object.bind(func)); methodsToBind.forEach((func) => Object.bind(func));
Object.freeze(this);
}
/** Object.freeze(this);
* Create a `Money` object with the given number of cents and the ISO currency unit }
* @static
* @param {number} amount
* @param {string} currency
* @return Money
* @memberof Money
*/
static fromCents(amount:MoneyAsJson): Money;
static fromCents(amount:Money): Money;
static fromCents(amount: number, currency: string) : Money;
static fromCents(amount: number|Money|MoneyAsJson, currency?: string): Money {
if (typeof amount === 'number') /**
return new Money(amount, currency); * Create a `Money` object with the given number of cents and the ISO currency unit
if (amount instanceof Money) * @static
return new Money(amount.amount, amount.currency); * @param {number} amount
else * @param {string} currency
return new Money(amount.amount, amount.currency); * @return Money
} * @memberof Money
*/
/** static fromCents(amount: MoneyAsJson): Money;
* Create a `Money` object with the given number if smallest monetary units and the ISO currency. Another name for the `fromCents` function. static fromCents(amount: Money): Money;
* @static static fromCents(amount: number, currency: string): Money;
* @memberof Money static fromCents(amount: number | Money | MoneyAsJson, currency?: string): Money {
*/
static fromSMU=Money.fromCents
/** if (typeof amount === 'number')
return new Money(amount, currency);
if (amount instanceof Money)
return new Money(amount.amount, amount.currency);
else
return new Money(amount.amount, amount.currency);
}
/**
* Create a `Money` object with the given number if smallest monetary units and the ISO currency. Another name for the `fromCents` function.
* @static
* @memberof Money
*/
static fromSMU = Money.fromCents
/**
* Returns true if the two instances of Money are equal, false otherwise. * Returns true if the two instances of Money are equal, false otherwise.
* *
* @param {Money} other * @param {Money} other
* @returns {Boolean} * @returns {Boolean}
*/ */
equals(other: Money): boolean { equals(other: Money): boolean {
assertType(other); assertType(other);
return this.amount === other.amount && return this.amount === other.amount &&
this.currency === other.currency; this.currency === other.currency;
} }
/** /**
* Adds the two objects together creating a new Money instance that holds the result of the operation. * Adds the two objects together creating a new Money instance that holds the result of the operation.
* *
* @param {Money} other * @param {Money} other
* @returns {Money} * @returns {Money}
*/ */
add(other: Money): Money { add(other: Money): Money {
assertType(other); assertType(other);
assertSameCurrency(this, other); assertSameCurrency(this, other);
return new Money(this.amount + other.amount, this.currency); return new Money(this.amount + other.amount, this.currency);
} }
/** /**
* Subtracts the two objects creating a new Money instance that holds the result of the operation. * Subtracts the two objects creating a new Money instance that holds the result of the operation.
* *
* @param {Money} other * @param {Money} other
* @returns {Money} * @returns {Money}
*/ */
subtract(other: Money): Money { subtract(other: Money): Money {
assertType(other); assertType(other);
assertSameCurrency(this, other); assertSameCurrency(this, other);
return new Money(this.amount - other.amount, this.currency); return new Money(this.amount - other.amount, this.currency);
} }
/** /**
* Multiplies the object by the multiplier returning a new Money instance that holds the result of the operation. * Multiplies the object by the multiplier returning a new Money instance that holds the result of the operation.
* *
* @param {number} multiplier * @param {number} multiplier
* @param {(x:number) => number} [fn=Math.round] * @param {(x:number) => number} [fn=Math.round]
* @returns {Money} * @returns {Money}
*/ */
multiply(multiplier: number, roundingFunction: (x:number) => number): Money { multiply(multiplier: number, roundingFunction: (x: number) => number): Money {
if (!isFunction(roundingFunction)) if (!isFunction(roundingFunction))
roundingFunction = Math.round; roundingFunction = Math.round;
assertOperand(multiplier); assertOperand(multiplier);
const amount = roundingFunction(this.amount * multiplier); const amount = roundingFunction(this.amount * multiplier);
return new Money(amount, this.currency); return new Money(amount, this.currency);
} }
/** /**
* Divides the object by the multiplier returning a new Money instance that holds the result of the operation. * Divides the object by the multiplier returning a new Money instance that holds the result of the operation.
* *
* @param {Number} divisor * @param {Number} divisor
* @param {(x:number) => number} [fn=Math.round] * @param {(x:number) => number} [fn=Math.round]
* @returns {Money} * @returns {Money}
*/ */
divide(divisor: number, fn?: (x: number) => number): Money { divide(divisor: number, fn?: (x: number) => number): Money {
if (!isFunction(fn)) if (!isFunction(fn))
fn = Math.round; fn = Math.round;
assertOperand(divisor); assertOperand(divisor);
const amount = fn(this.amount / divisor); const amount = fn(this.amount / divisor);
return new Money(amount, this.currency); return new Money(amount, this.currency);
} }
/** /**
* Allocates fund bases on the ratios provided returing an array of objects as a product of the allocation. * Allocates fund bases on the ratios provided returing an array of objects as a product of the allocation.
* *
* @param {Array} other * @param {Array} other
* @param {Money[]} * @param {Money[]}
*/ */
allocate(ratios: number[]): Money[] { allocate(ratios: number[]): Money[] {
let remainder = this.amount; let remainder = this.amount;
const results: Money[] = []; const results: Money[] = [];
let total = 0; let total = 0;
ratios.forEach(function (ratio) { ratios.forEach(function (ratio) {
total += ratio; total += ratio;
}); });
ratios.forEach(function (ratio) { ratios.forEach(function (ratio) {
const share = Math.floor(this.amount * ratio / total); const share = Math.floor(this.amount * ratio / total);
results.push(new Money(share, this.currency)); results.push(new Money(share, this.currency));
remainder -= share; remainder -= share;
}); });
for (let i = 0; remainder > 0; i++) { for (let i = 0; remainder > 0; i++) {
results[i] = new Money(results[i].amount + 1, results[i].currency); results[i] = new Money(results[i].amount + 1, results[i].currency);
remainder--; remainder--;
} }
return results; return results;
} }
/** /**
* Compares two instances of Money. * Compares two instances of Money.
* *
* @param {Money} other * @param {Money} other
* @returns {Number} * @returns {Number}
*/ */
compare(other: Money): number { compare(other: Money): number {
assertType(other); assertType(other);
assertSameCurrency(this, other); assertSameCurrency(this, other);
if (this.amount === other.amount) if (this.amount === other.amount)
return 0; return 0;
return this.amount > other.amount ? 1 : -1; return this.amount > other.amount ? 1 : -1;
} }
/** /**
* Checks whether the value represented by this object is greater than the other. * Checks whether the value represented by this object is greater than the other.
* *
* @param {Money} other * @param {Money} other
* @returns {boolean} * @returns {boolean}
*/ */
greaterThan(other: Money): boolean { greaterThan(other: Money): boolean {
return 1 === this.compare(other); return 1 === this.compare(other);
} }
/** /**
* Checks whether the value represented by this object is greater or equal to the other. * Checks whether the value represented by this object is greater or equal to the other.
* *
* @param {Money} other * @param {Money} other
* @returns {boolean} * @returns {boolean}
*/ */
greaterThanOrEqual(other: Money): boolean { greaterThanOrEqual(other: Money): boolean {
return 0 <= this.compare(other); return 0 <= this.compare(other);
} }
/** /**
* Checks whether the value represented by this object is less than the other. * Checks whether the value represented by this object is less than the other.
* *
* @param {Money} other * @param {Money} other
* @returns {boolean} * @returns {boolean}
*/ */
lessThan(other: Money): boolean { lessThan(other: Money): boolean {
return -1 === this.compare(other); return -1 === this.compare(other);
} }
/** /**
* Checks whether the value represented by this object is less than or equal to the other. * Checks whether the value represented by this object is less than or equal to the other.
* *
* @param {Money} other * @param {Money} other
* @returns {boolean} * @returns {boolean}
*/ */
lessThanOrEqual(other: Money): boolean { lessThanOrEqual(other: Money): boolean {
return 0 >= this.compare(other); return 0 >= this.compare(other);
} }
/** /**
* Returns true if the amount is zero. * Returns true if the amount is zero.
* *
* @returns {boolean} * @returns {boolean}
*/ */
isZero(): boolean { isZero(): boolean {
return this.amount === 0; return this.amount === 0;
} }
/** /**
* Returns true if the amount is positive. * Returns true if the amount is positive.
* *
* @returns {boolean} * @returns {boolean}
*/ */
isPositive(): boolean { isPositive(): boolean {
return this.amount > 0; return this.amount > 0;
} }
isNegative(): boolean { isNegative(): boolean {
return this.amount < 0; return this.amount < 0;
} }
/** /**
* Returns a serialised version of the instance. * Returns a serialised version of the instance.
* *
* @returns {{amount: number, currency: string}} * @returns {{amount: number, currency: string}}
*/ */
toJSON(): MoneyAsJson { toJSON(): MoneyAsJson {
return { return {
amount: this.amount, amount: this.amount,
currency: this.currency currency: this.currency
}; };
} }
} }