diff --git a/README.md b/README.md index 6671d94..c8c8971 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ This is a simple coupon creation project using NodeJS. [![license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/yusufshakeel/couponjs) -[![npm version](https://img.shields.io/badge/npm-0.2.0-blue.svg)](https://www.npmjs.com/package/couponjs) +[![npm version](https://img.shields.io/badge/npm-0.3.0-blue.svg)](https://www.npmjs.com/package/couponjs) [![Build Status](https://travis-ci.com/yusufshakeel/couponjs.svg?branch=master)](https://travis-ci.com/yusufshakeel/couponjs) # Getting Started @@ -41,6 +41,58 @@ const myCoupon = coupon.generate({ ``` Where, 8 in the above code represent the total number of characters that will be present in the coupon. +Range of `length`: +* Min: 1 +* Max: 128 + +If length is not passed then default value of 6 is considered. + +### Coupon with prefix +To generate a coupon with a given prefix we pass the following option to the `generate` method. +```javascript +const myCoupon = coupon.generate({ + prefix: 'SUPER' +}); +``` +The above code will generate coupon with prefix 'SUPER'. Default lenght of the coupon is 6. So, we will get coupon like the following. + +`SUPERAAAAAA` + +Note! Prefix characters are not counted. + +If we want to generate coupon of length 3 with prefix 'SUPER' then our option will look like the following. +```javascript +const myCoupon = coupon.generate({ + length: 3, + prefix: 'SUPER' +}); +``` +We will get coupon like the following `SUPERAAA`. + +### Coupon with suffix +To create coupon with suffix pass the following option. +```javascript +const myCoupon = coupon.generate({ + length: 3, + suffix: 'AWESOME' +}); +``` +The above code will generate coupon like the following `ZZZAWESOME`. + +Note! Characters of the suffix is not counted. If length is note specified then default value of 6 is considered as the length. + +### Coupon with prefix and suffix +To create coupon with prefix and suffix pass the following option. +```javascript +const myCoupon = coupon.generate({ + length: 6, + prefix: 'SUPER', + suffix: 'AWESOME' +}); +``` +The above code will generate coupon like the following `SUPERZZZZZZAWESOME`. + +Note! The characters of the prefix and suffix is not considered. If length is not specified then default value of 6 is considered. ## License It's free :smiley: diff --git a/app/constants.js b/app/constants.js index 14bbeb7..767cfdf 100644 --- a/app/constants.js +++ b/app/constants.js @@ -1,7 +1,11 @@ /** - * @type {{DEFAULT_LENGTH: number, ALPHABET_UPPERCASE: string}} + * @type {{DEFAULT_LENGTH: number, DEFAULT_PREFIX: string, DEFAULT_SUFFIX: string, ALPHABET_UPPERCASE: string}} */ module.exports = { + MAX_LENGTH: 128, + MIN_LENGTH: 1, DEFAULT_LENGTH: 6, + DEFAULT_PREFIX: '', + DEFAULT_SUFFIX: '', ALPHABET_UPPERCASE: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' }; \ No newline at end of file diff --git a/app/engine.js b/app/engine.js index 8e7d941..21404c2 100644 --- a/app/engine.js +++ b/app/engine.js @@ -1,21 +1,40 @@ -const { DEFAULT_LENGTH } = require('./constants.js'); +const {MAX_LENGTH, MIN_LENGTH, DEFAULT_LENGTH, DEFAULT_PREFIX, DEFAULT_SUFFIX} = require('./constants.js'); /** * Engine to produce coupon. * @param {string} characters This is the set of characters used to generate coupon. * @param {function} randomInteger This is the function that will generate random integer value. - * @param {number} length This is the length of the coupon. + * @param {number} length This is the length of the coupon excluding prefix and suffix characters if any. + * @param {string} prefix This is the set of characters that is added at the start of the coupon. + * @param {string} suffix This is the set of characters that is added at the end of the coupon. * @constructor */ -const Engine = function (characters, randomInteger, length = DEFAULT_LENGTH) { +const Engine = function (characters, randomInteger, length = DEFAULT_LENGTH, prefix = DEFAULT_PREFIX, suffix = DEFAULT_SUFFIX) { + /** + * This will validate options + * @param length + */ + const validate = function ({length}) { + if (length <= MIN_LENGTH) throw new Error(`Minimum value for "length" is ${MIN_LENGTH}.`); + if (length >= MAX_LENGTH) throw new Error(`Maximum value for "length" is ${MAX_LENGTH}.`); + }; + + /** + * This will return array of characters. + * @returns {string[]} + */ function characterSet() { return characters.split(''); } + /** + * This will generate the coupon. + * @returns {string} + */ function generateCoupon() { const generatedCouponCharacters = []; const charSet = characterSet(); - for(let i = 0; i < length; i++) { + for (let i = 0; i < length; i++) { generatedCouponCharacters.push( charSet[randomInteger(0, length - 1)] ); @@ -28,8 +47,9 @@ const Engine = function (characters, randomInteger, length = DEFAULT_LENGTH) { * @returns {string} */ this.run = function () { - return generateCoupon(); - } + validate({length}); + return `${prefix}${generateCoupon()}${suffix}`; + }; }; module.exports = Engine; \ No newline at end of file diff --git a/app/option.js b/app/option.js index 9f0fc5b..3c1ef88 100644 --- a/app/option.js +++ b/app/option.js @@ -1,6 +1,8 @@ -const { DEFAULT_LENGTH, ALPHABET_UPPERCASE } = require('./constants.js'); +const {DEFAULT_LENGTH, DEFAULT_PREFIX, DEFAULT_SUFFIX, ALPHABET_UPPERCASE} = require('./constants.js'); module.exports = { length: DEFAULT_LENGTH, + prefix: DEFAULT_PREFIX, + suffix: DEFAULT_SUFFIX, characters: ALPHABET_UPPERCASE }; \ No newline at end of file diff --git a/index.js b/index.js index 9514a19..676ef27 100644 --- a/index.js +++ b/index.js @@ -7,6 +7,7 @@ const randomInteger = require('./app/random-integer.js'); * @constructor */ const Coupon = function () { + /** * This will generate coupons. * @@ -14,8 +15,8 @@ const Coupon = function () { * @returns {string} */ this.generate = function (option) { - const { length, characters } = Object.assign(defaultOptions, option); - const engine = new Engine(characters, randomInteger, length); + const {length, characters, prefix, suffix} = Object.assign({}, defaultOptions, option); + const engine = new Engine(characters, randomInteger, length, prefix, suffix); return engine.run(); }; }; diff --git a/package-lock.json b/package-lock.json index 47528eb..94a56cd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "couponjs", - "version": "0.2.0", + "version": "0.3.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 1dec177..34a62b6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "couponjs", - "version": "0.2.0", + "version": "0.3.0", "description": "This is a simple coupon creation project using NodeJS.", "main": "index.js", "scripts": { diff --git a/tests/app/engine.test.js b/tests/app/engine.test.js index 26d75c2..f6daa48 100644 --- a/tests/app/engine.test.js +++ b/tests/app/engine.test.js @@ -1,7 +1,31 @@ const Engine = require('../../app/engine.js'); +test('Should throw error if length is less than 1', () => { + expect(() => { + const characters = 'A'; + const mockRandomInteger = jest.fn((min, max) => { + return 0; + }); + const engine = new Engine(characters, mockRandomInteger, 0); + engine.run(); + throw new Error('Should have failed.'); + }).toThrow('Minimum value for "length" is 1.'); +}); + +test('Should throw error if length is greater than 128', () => { + expect(() => { + const characters = 'A'; + const mockRandomInteger = jest.fn((min, max) => { + return 0; + }); + const engine = new Engine(characters, mockRandomInteger, 200); + engine.run(); + throw new Error('Should have failed.'); + }).toThrow('Maximum value for "length" is 128.'); +}); + test('Should return AAAAAA as coupon when character set is "A" and randomInteger generates always 0', () => { - const characters = "A"; + const characters = 'A'; const mockRandomInteger = jest.fn((min, max) => { return 0; }); @@ -10,7 +34,7 @@ test('Should return AAAAAA as coupon when character set is "A" and randomInteger }); test('Should return zzzzzz as coupon when character set is "z" and randomInteger generates always 0', () => { - const characters = "z"; + const characters = 'z'; const mockRandomInteger = jest.fn((min, max) => { return 0; }); @@ -19,10 +43,37 @@ test('Should return zzzzzz as coupon when character set is "z" and randomInteger }); test('Should return aaa as coupon when character set is "a" and rendomInteger generates always 0 and length is 3', () => { - const characters = "a"; + const characters = 'a'; const mockRandomInteger = jest.fn((min, max) => { return 0; }); const engine = new Engine(characters, mockRandomInteger, 3); expect(engine.run()).toBe('aaa'); +}); + +test('Should return PREFIXaaa as coupon when character set is "a" and rendomInteger generates always 0 and length is 3', () => { + const characters = 'a'; + const mockRandomInteger = jest.fn((min, max) => { + return 0; + }); + const engine = new Engine(characters, mockRandomInteger, 3, 'PREFIX'); + expect(engine.run()).toBe('PREFIXaaa'); +}); + +test('Should return aaaSUFFIX as coupon when character set is "a" and rendomInteger generates always 0 and length is 3', () => { + const characters = 'a'; + const mockRandomInteger = jest.fn((min, max) => { + return 0; + }); + const engine = new Engine(characters, mockRandomInteger, 3, '', 'SUFFIX'); + expect(engine.run()).toBe('aaaSUFFIX'); +}); + +test('Should return PREFIXaaaSUFFIX as coupon when character set is "a" and rendomInteger generates always 0 and length is 3', () => { + const characters = 'a'; + const mockRandomInteger = jest.fn((min, max) => { + return 0; + }); + const engine = new Engine(characters, mockRandomInteger, 3, 'PREFIX', 'SUFFIX'); + expect(engine.run()).toBe('PREFIXaaaSUFFIX'); }); \ No newline at end of file diff --git a/tests/index.test.js b/tests/index.test.js index 6186b90..36171a6 100644 --- a/tests/index.test.js +++ b/tests/index.test.js @@ -8,6 +8,64 @@ test('Should generate coupon code using uppercase alphabet A-Z of length 6.', () test('Should generate coupon code using uppercase alphabet A-Z of length 8.', () => { const coupon = new Coupon(); - const result = coupon.generate({ length: 8 }); + const result = coupon.generate({length: 8}); expect(/^[A-Z]{8}$/.test(result)).toBeTruthy(); }); + +test('Should generate coupon code using uppercase alphabet A-Z of length 6,7,8,9,10.', () => { + const coupon = new Coupon(); + expect(/^[A-Z]{6}$/.test(coupon.generate({length: 6}))).toBeTruthy(); + expect(/^[A-Z]{7}$/.test(coupon.generate({length: 7}))).toBeTruthy(); + expect(/^[A-Z]{8}$/.test(coupon.generate({length: 8}))).toBeTruthy(); + expect(/^[A-Z]{9}$/.test(coupon.generate({length: 9}))).toBeTruthy(); + expect(/^[A-Z]{10}$/.test(coupon.generate({length: 10}))).toBeTruthy(); +}); + +test('Should generate coupon code using uppercase alphabet A-Z of length 6 including prefix "SUPER"', () => { + const coupon = new Coupon(); + const result = coupon.generate({length: 6, prefix: 'SUPER'}); + expect(/^SUPER[A-Z]{6}$/.test(result)).toBeTruthy(); +}); + +test('Should generate coupon code using uppercase alphabet A-Z of length 6 including prefix "SUPER" and suffix "AWESOME"', () => { + const coupon = new Coupon(); + const result = coupon.generate({length: 6, prefix: 'SUPER', suffix: 'AWESOME'}); + expect(/^SUPER[A-Z]{6}AWESOME$/.test(result)).toBeTruthy(); +}); + +test('Should generate coupon code using uppercase alphabet A-Z of length 6 including suffix "AWESOME"', () => { + const coupon = new Coupon(); + const result = coupon.generate({length: 6, suffix: 'AWESOME'}); + expect(/^[A-Z]{6}AWESOME$/.test(result)).toBeTruthy(); +}); + +test('Should generate coupon code using uppercase alphabet A-Z of length 6,7,8,9,10 including prefix "SUPER"', () => { + const coupon = new Coupon(); + expect(/^SUPER[A-Z]{6}$/.test(coupon.generate({length: 6, prefix: 'SUPER'}))).toBeTruthy(); + expect(/^SUPER[A-Z]{7}$/.test(coupon.generate({length: 7, prefix: 'SUPER'}))).toBeTruthy(); + expect(/^SUPER[A-Z]{8}$/.test(coupon.generate({length: 8, prefix: 'SUPER'}))).toBeTruthy(); + expect(/^SUPER[A-Z]{9}$/.test(coupon.generate({length: 9, prefix: 'SUPER'}))).toBeTruthy(); + expect(/^SUPER[A-Z]{10}$/.test(coupon.generate({length: 10, prefix: 'SUPER'}))).toBeTruthy(); +}); + +test('Should generate coupon code using uppercase alphabet A-Z of length 6,7,8,9,10 including prefix "SUPER" and suffix "AWESOME"', () => { + const coupon = new Coupon(); + expect(/^SUPER[A-Z]{6}AWESOME$/.test(coupon.generate({length: 6, prefix: 'SUPER', suffix: 'AWESOME'}))).toBeTruthy(); + expect(/^SUPER[A-Z]{7}AWESOME$/.test(coupon.generate({length: 7, prefix: 'SUPER', suffix: 'AWESOME'}))).toBeTruthy(); + expect(/^SUPER[A-Z]{8}AWESOME$/.test(coupon.generate({length: 8, prefix: 'SUPER', suffix: 'AWESOME'}))).toBeTruthy(); + expect(/^SUPER[A-Z]{9}AWESOME$/.test(coupon.generate({length: 9, prefix: 'SUPER', suffix: 'AWESOME'}))).toBeTruthy(); + expect(/^SUPER[A-Z]{10}AWESOME$/.test(coupon.generate({ + length: 10, + prefix: 'SUPER', + suffix: 'AWESOME' + }))).toBeTruthy(); +}); + +test('Should generate coupon code using uppercase alphabet A-Z of length 6,7,8,9,10 including suffix "AWESOME"', () => { + const coupon = new Coupon(); + expect(/^[A-Z]{6}AWESOME$/.test(coupon.generate({length: 6, suffix: 'AWESOME'}))).toBeTruthy(); + expect(/^[A-Z]{7}AWESOME$/.test(coupon.generate({length: 7, suffix: 'AWESOME'}))).toBeTruthy(); + expect(/^[A-Z]{8}AWESOME$/.test(coupon.generate({length: 8, suffix: 'AWESOME'}))).toBeTruthy(); + expect(/^[A-Z]{9}AWESOME$/.test(coupon.generate({length: 9, suffix: 'AWESOME'}))).toBeTruthy(); + expect(/^[A-Z]{10}AWESOME$/.test(coupon.generate({length: 10, suffix: 'AWESOME'}))).toBeTruthy(); +});