diff --git a/.eslintrc.yml b/.eslintrc.yml index 6919369..e6b9f01 100644 --- a/.eslintrc.yml +++ b/.eslintrc.yml @@ -28,6 +28,8 @@ rules: '@typescript-eslint/non-nullable-type-assertion-style': 'off' '@typescript-eslint/promise-function-async': 'off' '@typescript-eslint/strict-boolean-expressions': ['error', allowNullableObject: true] + '@typescript-eslint/space-before-function-paren': 'off' + '@typescript-eslint/member-delimiter-style': 'off' 'import/no-default-export': 'error' 'import/no-unresolved': 'error' 'jsdoc/check-param-names': ['error', checkDestructured: false] diff --git a/source/dice.ts b/source/dice.ts index a22c51f..109f68a 100644 --- a/source/dice.ts +++ b/source/dice.ts @@ -30,7 +30,7 @@ export const compute = ({ quantity, separator, sides, modifier }: RollSpec): num /** * A factory function for creating dice rolls. * - * @param notation The notation string representing the type of roll to create, e.g. "2d6+3". + * @param notation - The notation string representing the type of roll to create, e.g. "2d6+3". * @returns An object with methods and properties representing the result of a dice roll. */ export const Roll = (notation: string): RollResult => { @@ -39,15 +39,27 @@ export const Roll = (notation: string): RollResult => { return { values: rolls, + /** + * The sum of all dice rolls. + */ get sum(): number { return rolls.reduce(sum, 0) }, + /** + * The min value of all dice rolls. + */ get min(): number { return rolls.sort(sortAsc)[0] ?? 0 }, + /** + * The max value of all dice rolls. + */ get max(): number { return rolls.sort(sortDesc)[0] ?? 0 }, + /** + * Is any of the dice rolls a critical hit or a critical failure. + */ get isCritical(): boolean { return rolls.includes(1) || rolls.includes(20) } diff --git a/source/dice.unit.test.ts b/source/dice.unit.test.ts index b4da276..2f74db7 100644 --- a/source/dice.unit.test.ts +++ b/source/dice.unit.test.ts @@ -10,6 +10,8 @@ describe('dice', () => { describe('roll', () => { it('should return a number when given a valid dice string with only one die', () => { + expect.assertions(3) + const spy = vi.spyOn(parser, 'parse') const shape = '1d6' @@ -19,21 +21,27 @@ describe('dice', () => { }) it('should return a number when given a valid dice string with multiple dice', () => { + expect.assertions(1) + const shape = '2d10' expect(typeof roll(shape)).toBe('number') }) it('should correctly sum the rolls', () => { + expect.assertions(2) + // mock the rand function to return known values const spy = vi.spyOn(random, 'rand').mockReturnValueOnce(3).mockReturnValueOnce(5) const result = roll('2d6') - expect(result).toStrictEqual(8) + expect(result).toBe(8) expect(spy).toHaveBeenCalledTimes(2) }) it('should change min value when given `z` as separator', () => { + expect.assertions(2) + // mock the rand function to return known values const spy = vi.spyOn(random, 'rand') const shape = '3z8' @@ -43,6 +51,8 @@ describe('dice', () => { }) it('should throw an error when given an invalid dice string', () => { + expect.assertions(1) + const shape = 'invalid' expect(() => roll(shape)).toThrow(`Invalid dice notation '${shape}'`) }) @@ -51,6 +61,8 @@ describe('dice', () => { describe('roll factory', () => { describe('sum', () => { it('should correctly calculate the sum of the rolls', () => { + expect.assertions(2) + const roll = Roll('2d6') expect(roll.sum).toBeGreaterThanOrEqual(2) expect(roll.sum).toBeLessThanOrEqual(12) @@ -59,6 +71,8 @@ describe('dice', () => { describe('min', () => { it('should correctly identify the minimum roll', () => { + expect.assertions(1) + vi.spyOn(random, 'rand') .mockReturnValueOnce(1) .mockReturnValueOnce(2) @@ -72,6 +86,8 @@ describe('dice', () => { describe('max', () => { it('should correctly identify the maximum roll', () => { + expect.assertions(1) + vi.spyOn(random, 'rand') .mockReturnValueOnce(1) .mockReturnValueOnce(2) @@ -85,6 +101,8 @@ describe('dice', () => { describe('isCritical', () => { it('should return true if a critical roll was made with nat 20', () => { + expect.assertions(1) + vi.spyOn(random, 'rand').mockReturnValueOnce(20) const roll = Roll('1d20') @@ -92,6 +110,8 @@ describe('dice', () => { }) it('should return true if a critical roll was made with nat 1', () => { + expect.assertions(1) + vi.spyOn(random, 'rand').mockReturnValueOnce(1) const roll = Roll('1d20') @@ -99,6 +119,7 @@ describe('dice', () => { }) it('should return false if a critical roll was not made', () => { + expect.assertions(1) vi.spyOn(random, 'rand').mockReturnValueOnce(2) const roll = Roll('1d20') diff --git a/source/parser.ts b/source/parser.ts index d01b5db..58e50fc 100644 --- a/source/parser.ts +++ b/source/parser.ts @@ -9,7 +9,6 @@ const regex = /^(?:(?\d+)|)(?d|z)(?[1-9]\d*)(? { const match = regex.exec(notation.toLowerCase()) if (match?.groups == null) { diff --git a/source/parser.unit.test.ts b/source/parser.unit.test.ts index 633ea53..cc8e86c 100644 --- a/source/parser.unit.test.ts +++ b/source/parser.unit.test.ts @@ -16,8 +16,10 @@ describe('parse', () => { ] it.each(validInputs)( - `should return the correct Dice object when passed a valid shape - '%j'`, + "should return the correct Dice object when passed a valid shape - '%j'", ({ input, expected }) => { + expect.assertions(1) + const actual = parse(input) expect(actual).toStrictEqual(expected) } @@ -25,7 +27,9 @@ describe('parse', () => { const invalidInputs = ['', 'd', '4', '1dx', 'xd1', '-2d10', 'd-1', '2d0', '2d6d', '3d+2'] - it.each(invalidInputs)(`should throw an error when passed an invalid shape - '%s'`, (input) => { + it.each(invalidInputs)("should throw an error when passed an invalid shape - '%s'", (input) => { + expect.assertions(1) + expect(() => parse(input)).toThrow(`Invalid dice notation '${input}'`) }) }) diff --git a/source/utils/array.ts b/source/utils/array.ts index 9593a7b..4a1bf93 100644 --- a/source/utils/array.ts +++ b/source/utils/array.ts @@ -1,3 +1,26 @@ +/** + * A compare function that can be used with Array.prototype.sort to sort an array of numbers in ascending order. + * + * @param a - The first number for comparison. + * @param b - The second number for comparison. + * @returns The difference between `a` and `b`. + */ export const sortAsc = (a: number, b: number): number => a - b + +/** + * A compare function that can be used with Array.prototype.sort to sort an array of numbers in descending order. + * + * @param a - The first number for comparison. + * @param b - The second number for comparison. + * @returns The difference between `b` and `a`. + */ export const sortDesc = (a: number, b: number): number => b - a + +/** + * Calculates the sum of two numbers. + * + * @param accumulator - The initial value of the sum. + * @param value - The value to be added to the sum. + * @returns The sum of the accumulator and the value. + */ export const sum = (accumulator: number, value: number): number => accumulator + value