diff --git a/gulpfile.js b/gulpfile.js index c2c86f0..f60156d 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -17,7 +17,9 @@ var REQUIRE_PATTERN = /((?:var |,)[^=]+=\s*require\([^\)]+\);?\n?)/g; var EXPORT_PATTERN = /((?:module\.)?exports\s*=\s*[^,;]+;?\n?)/g; var files = [ + './src/maxSafeInteger.js', './src/isFinite.js', + './src/isSafeNumber.js', './src/makeOrdinal.js', './src/toOrdinal.js', './src/toWords.js', diff --git a/spec/isSafeNumberSpec.js b/spec/isSafeNumberSpec.js new file mode 100644 index 0000000..cd15463 --- /dev/null +++ b/spec/isSafeNumberSpec.js @@ -0,0 +1,30 @@ +'use strict'; + +var MAX_SAFE_INTEGER = require('../src/maxSafeInteger'); +var isSafeNumber = require('../src/isSafeNumber'); + +describe('isSafeNumber', function() { + it('should return true if input is a number between -MAX_SAFE_INTEGER and MAX_SAFE_INTEGER (including)', function() { + expect(isSafeNumber(-MAX_SAFE_INTEGER)).toBe(true); + expect(isSafeNumber(-1)).toBe(true); + expect(isSafeNumber(0)).toBe(true); + expect(isSafeNumber(1)).toBe(true); + expect(isSafeNumber(MAX_SAFE_INTEGER)).toBe(true); + }); + it('should return false if input is too large or too small', function() { + var unsafe = MAX_SAFE_INTEGER + 100; + + expect(isSafeNumber(unsafe)).toBe(false); + expect(isSafeNumber(-unsafe)).toBe(false); + }); + it('should return false if input is not a number', function() { + expect(isSafeNumber()).toBe(false); + expect(isSafeNumber(null)).toBe(false); + expect(isSafeNumber([])).toBe(false); + expect(isSafeNumber({})).toBe(false); + expect(isSafeNumber('')).toBe(false); + expect(isSafeNumber('x')).toBe(false); + expect(isSafeNumber(function() {})).toBe(false); + expect(isSafeNumber(NaN)).toBe(false); + }); +}); diff --git a/spec/toOrdinalSpec.js b/spec/toOrdinalSpec.js index af88224..124c4a8 100644 --- a/spec/toOrdinalSpec.js +++ b/spec/toOrdinalSpec.js @@ -1,6 +1,7 @@ 'use strict'; var toOrdinal = typeof require !== 'undefined' ? require('../src/toOrdinal') : window.numberToWords.toOrdinal; +var MAX_SAFE_INTEGER = 9007199254740991; describe('toOrdinal', function () { var tests = [ @@ -36,4 +37,16 @@ describe('toOrdinal', function () { } tests.forEach(addTest); + + it('should throw a RangeError if input is greater or lesser than MAX_SAFE_INTEGER', function() { + var unsafe = MAX_SAFE_INTEGER + 100; + + expect(function() { + toOrdinal(unsafe); + }).toThrowError(/Input is not a safe number/); + + expect(function() { + toOrdinal(-unsafe); + }).toThrowError(/Input is not a safe number/); + }); }); diff --git a/spec/toWordsSpec.js b/spec/toWordsSpec.js index 0fd0927..ee9e1f4 100644 --- a/spec/toWordsSpec.js +++ b/spec/toWordsSpec.js @@ -1,5 +1,6 @@ 'use strict'; +var MAX_SAFE_INTEGER = 9007199254740991; var toWords = typeof require !== 'undefined' ? require('../src/toWords') : window.numberToWords.toWords; describe('toWords', function () { @@ -88,8 +89,8 @@ describe('toWords', function () { expect: 'five quadrillion, five hundred fifty-five trillion, five hundred fifty-five billion, five hundred fifty-five million, five hundred fifty-five thousand, five hundred fifty-five' }, { - input: 9007199254740992, - expect: 'nine quadrillion, seven trillion, one hundred ninety-nine billion, two hundred fifty-four million, seven hundred forty thousand, nine hundred ninety-two' + input: MAX_SAFE_INTEGER, + expect: 'nine quadrillion, seven trillion, one hundred ninety-nine billion, two hundred fifty-four million, seven hundred forty thousand, nine hundred ninety-one' } ]; @@ -111,6 +112,18 @@ describe('toWords', function () { expect(toWords(30, true)).toEqual('thirtieth'); expect(toWords(123, true)).toEqual('one hundred twenty-third'); }); + + it('should throw a RangeError if input is greater or lesser than MAX_SAFE_INTEGER', function() { + var unsafe = MAX_SAFE_INTEGER + 100; + + expect(function() { + toWords(unsafe); + }).toThrowError(/Input is not a safe number/); + + expect(function() { + toWords(-unsafe); + }).toThrowError(/Input is not a safe number/); + }); }); function formatNumber(number) { diff --git a/src/isSafeNumber.js b/src/isSafeNumber.js new file mode 100644 index 0000000..ae43920 --- /dev/null +++ b/src/isSafeNumber.js @@ -0,0 +1,9 @@ +'use strict'; + +var MAX_SAFE_INTEGER = require('./maxSafeInteger'); + +function isSafeNumber(value) { + return typeof value === 'number' && Math.abs(value) <= MAX_SAFE_INTEGER; +} + +module.exports = isSafeNumber; diff --git a/src/maxSafeInteger.js b/src/maxSafeInteger.js new file mode 100644 index 0000000..ff6d6f6 --- /dev/null +++ b/src/maxSafeInteger.js @@ -0,0 +1,5 @@ +'use strict'; + +var MAX_SAFE_INTEGER = 9007199254740991; + +module.exports = MAX_SAFE_INTEGER; diff --git a/src/toOrdinal.js b/src/toOrdinal.js index 666d88d..80528b2 100644 --- a/src/toOrdinal.js +++ b/src/toOrdinal.js @@ -1,6 +1,7 @@ 'use strict'; var isFinite = require('./isFinite'); +var isSafeNumber = require('./isSafeNumber'); /** * Converts an integer into a string with an ordinal postfix. @@ -11,7 +12,17 @@ var isFinite = require('./isFinite'); */ function toOrdinal(number) { var num = parseInt(number, 10); - if (!isFinite(num)) throw new TypeError('Not a finite number: ' + number + ' (' + typeof number + ')'); + + if (!isFinite(num)) { + throw new TypeError( + 'Not a finite number: ' + number + ' (' + typeof number + ')' + ); + } + if (!isSafeNumber(num)) { + throw new RangeError( + 'Input is not a safe number, it’s either too large or too small.' + ); + } var str = String(num); var lastTwoDigits = Math.abs(num % 100); var betweenElevenAndThirteen = lastTwoDigits >= 11 && lastTwoDigits <= 13; diff --git a/src/toWords.js b/src/toWords.js index ee1b204..4325613 100644 --- a/src/toWords.js +++ b/src/toWords.js @@ -2,6 +2,7 @@ var makeOrdinal = require('./makeOrdinal'); var isFinite = require('./isFinite'); +var isSafeNumber = require('./isSafeNumber'); var TEN = 10; var ONE_HUNDRED = 100; @@ -32,7 +33,17 @@ var TENTHS_LESS_THAN_HUNDRED = [ function toWords(number, asOrdinal) { var words; var num = parseInt(number, 10); - if (!isFinite(num)) throw new TypeError('Not a finite number: ' + number + ' (' + typeof number + ')'); + + if (!isFinite(num)) { + throw new TypeError( + 'Not a finite number: ' + number + ' (' + typeof number + ')' + ); + } + if (!isSafeNumber(num)) { + throw new RangeError( + 'Input is not a safe number, it’s either too large or too small.' + ); + } words = generateWords(num); return asOrdinal ? makeOrdinal(words) : words; }