diff --git a/src/duration.test.ts b/src/duration.test.ts index 02782cf..8a06f0a 100644 --- a/src/duration.test.ts +++ b/src/duration.test.ts @@ -8,4 +8,29 @@ describe('parseDuration', () => { expect(parseDuration('5m')).toBe(300_000); expect(parseDuration('2h')).toBe(7_200_000); }); + + it('accepts zero', () => { + expect(parseDuration('0s')).toBe(0); + }); + + it('ignores surrounding whitespace', () => { + expect(parseDuration(' 5m ')).toBe(300_000); + expect(parseDuration('\t30s\n')).toBe(30_000); + }); + + it.each([ + ['5x'], + [''], + [' '], + ['5'], + ['m'], + ['-5s'], + ['1.5s'], + ['5 m'], + ['5M'], + ['5mm'], + ])('throws RangeError naming %j', (bad) => { + expect(() => parseDuration(bad)).toThrow(RangeError); + expect(() => parseDuration(bad)).toThrow(JSON.stringify(bad)); + }); }); diff --git a/src/duration.ts b/src/duration.ts index b1fd194..5f34383 100644 --- a/src/duration.ts +++ b/src/duration.ts @@ -7,6 +7,11 @@ const UNIT_MS: Record = { /** Parse "250ms", "30s", "5m", "2h" into milliseconds. */ export function parseDuration(input: string): number { - const match = /^(\d+)(ms|s|m|h)$/.exec(input); - return Number(match![1]) * UNIT_MS[match![2]]; + const match = /^(\d+)(ms|s|m|h)$/.exec(input.trim()); + if (!match) { + throw new RangeError( + `invalid duration ${JSON.stringify(input)}: expected an integer followed by ms, s, m or h`, + ); + } + return Number(match[1]) * UNIT_MS[match[2]]; }