Skip to content

Commit

Permalink
Merge pull request #4 from yusufshakeel/dev
Browse files Browse the repository at this point in the history
v0.4.0
  • Loading branch information
yusufshakeel authored Mar 14, 2020
2 parents f61a446 + ba153fd commit 6c4e5ba
Show file tree
Hide file tree
Showing 11 changed files with 326 additions and 51 deletions.
73 changes: 61 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.3.0-blue.svg)](https://www.npmjs.com/package/couponjs)
[![npm version](https://img.shields.io/badge/npm-0.4.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
Expand All @@ -11,12 +11,6 @@ Add this to your project using npm.
> npm i couponjs
```

## Tests
Test code of this project is inside the `tests` directory.

Using the following for testing:
* Jest

## Generate coupon
Create an object of Coupon.
```javascript
Expand All @@ -32,7 +26,7 @@ const myCoupon = coupon.generate();

By default, `generate` will return coupon code of length 6 using uppercase alphabet.

### Coupon of length N
## Coupon of length N
To generate coupon of a given length we pass the following option to the `generate` method.
```javascript
const myCoupon = coupon.generate({
Expand All @@ -47,7 +41,7 @@ Range of `length`:

If length is not passed then default value of 6 is considered.

### Coupon with prefix
## 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({
Expand All @@ -69,7 +63,7 @@ const myCoupon = coupon.generate({
```
We will get coupon like the following `SUPERAAA`.

### Coupon with suffix
## Coupon with suffix
To create coupon with suffix pass the following option.
```javascript
const myCoupon = coupon.generate({
Expand All @@ -79,9 +73,9 @@ const myCoupon = coupon.generate({
```
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.
Note! Characters of the suffix is not counted. If length is not specified then default value of 6 is considered as the length.

### Coupon with prefix and suffix
## Coupon with prefix and suffix
To create coupon with prefix and suffix pass the following option.
```javascript
const myCoupon = coupon.generate({
Expand All @@ -94,6 +88,61 @@ 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.

## Coupon with builtIn characterSet
To create coupon code with builtIn characterSet pass the following option.
```javascript
const myCoupon = coupon.generate({
characterSet: {
builtIn: ['charSetName']
}
});
```
Where, `charSetName` is any one of the following names.

- `CHARSET_ALPHA` -- which consists of uppercase alphabet characters A-Z
- `CHARSET_ALPHA_LOWER` -- which consists of lowercase alphabet characters a-z
- `CHARSET_DIGIT` -- which consists of digits 0-9

Example: If we want uppercase and digit we can pass the following.
```javascript
const myCoupon = coupon.generate({
characterSet: {
builtIn: ['CHARSET_ALPHA', 'CHARSET_DIGIT']
}
});
```

## Coupon with custom characterSet
To use custom characters to generate coupons pass the following option.
```javascript
const myCoupon = coupon.generate({
characterSet: {
custom: ['customChar']
}
});
```
Where, `customChar` is any custom characters that you wish to use.

Example: To use `ABC`, `xyz` and `01234` in coupon pass the following.
```javascript
const myCoupon = coupon.generate({
characterSet: {
custom: ['ABC', 'xyz', '01234']
}
});
```

## Coupons using builtIn and custom characterSet
Example: Following option will use digit `0-9` and alphabet `ABC`.
```javascript
const myCoupon = coupon.generate({
characterSet: {
builtIn: ['CHARSET_DIGIT'],
custom: ['ABC']
}
});
```

## License
It's free :smiley:

Expand Down
41 changes: 41 additions & 0 deletions app/character-set-builder.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
const { CHARSET_ALPHA, CHARSET_ALPHA_LOWER, CHARSET_DIGIT, ALPHABET_UPPERCASE, ALPHABET_LOWERCASE, DIGIT} = require('./constants.js');

/**
* This will return the characters based on the character set name.
* @param {string} charSet This is the name of the character set.
* @returns {string} String of characters.
*/
function characters (charSet) {
switch(charSet) {
case CHARSET_ALPHA: return ALPHABET_UPPERCASE;
case CHARSET_ALPHA_LOWER: return ALPHABET_LOWERCASE;
case CHARSET_DIGIT: return DIGIT;
default: throw new Error(`Invalid builtIn characterSet specified. Allowed values ["${CHARSET_ALPHA}", "${CHARSET_ALPHA_LOWER}", "${CHARSET_DIGIT}"]`);
}
}

/**
* This will generate a string of unique characters based on the options provided.
* @param {object} characterSetOptions The options to build the character set.
* @returns {string} The set of characters based on the options provided.
*/
function characterSetBuilder(characterSetOptions) {

const { builtIn = [], custom = [] } = characterSetOptions;

const builtInCharacters = builtIn.reduce((chars, charSet) => {
return `${chars}${characters(charSet)}`
}, '');

const customCharacters = custom.reduce((chars, charSet) => {
return `${chars}${charSet}`
}, '');

const uniqueCharacters = `${builtInCharacters}${customCharacters}`.split('').reduce((characters, character) => {
return characters + (characters.indexOf(character) === -1 ? character : '');
}, '');

return `${uniqueCharacters}`;
}

module.exports = characterSetBuilder;
9 changes: 7 additions & 2 deletions app/constants.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
/**
* @type {{DEFAULT_LENGTH: number, DEFAULT_PREFIX: string, DEFAULT_SUFFIX: string, ALPHABET_UPPERCASE: string}}
* @type {{MAX_LENGTH: number, MIN_LENGTH: number, DEFAULT_LENGTH: number, DEFAULT_PREFIX: string, DEFAULT_SUFFIX: string, ALPHABET_UPPERCASE: string, ALPHABET_LOWERCASE: string, DIGIT: string, CHARSET_ALPHA: string, CHARSET_ALPHA_LOWER: string, CHARSET_DIGIT: string}}
*/
module.exports = {
MAX_LENGTH: 128,
MIN_LENGTH: 1,
DEFAULT_LENGTH: 6,
DEFAULT_PREFIX: '',
DEFAULT_SUFFIX: '',
ALPHABET_UPPERCASE: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
ALPHABET_UPPERCASE: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
ALPHABET_LOWERCASE: 'abcdefghijklmnopqrstuvwxyz',
DIGIT: '0123456789',
CHARSET_ALPHA: 'CHARSET_ALPHA',
CHARSET_ALPHA_LOWER: 'CHARSET_ALPHA_LOWER',
CHARSET_DIGIT: 'CHARSET_DIGIT'
};
23 changes: 9 additions & 14 deletions app/engine.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
const {MAX_LENGTH, MIN_LENGTH, DEFAULT_LENGTH, DEFAULT_PREFIX, DEFAULT_SUFFIX} = require('./constants.js');
const characterSetBuilder = require('./character-set-builder.js');

/**
* Engine to produce coupon.
* @param {string} characters This is the set of characters used to generate coupon.
* @param {object} characterSetOption This is the set of character set options 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 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, prefix = DEFAULT_PREFIX, suffix = DEFAULT_SUFFIX) {
const Engine = function (characterSetOption, randomInteger, length = DEFAULT_LENGTH, prefix = DEFAULT_PREFIX, suffix = DEFAULT_SUFFIX) {

/**
* This will validate options
Expand All @@ -19,27 +21,20 @@ const Engine = function (characters, randomInteger, length = DEFAULT_LENGTH, pre
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 characters = characterSetBuilder(characterSetOption).split('');
const charactersLength = characters.length;
const generatedCouponCharacters = [];
const charSet = characterSet();
for (let i = 0; i < length; i++) {
generatedCouponCharacters.push(
charSet[randomInteger(0, length - 1)]
characters[randomInteger(0, charactersLength - 1)]
);
}
return generatedCouponCharacters.join('');
return `${prefix}${generatedCouponCharacters.join('')}${suffix}`;
}

/**
Expand All @@ -48,7 +43,7 @@ const Engine = function (characters, randomInteger, length = DEFAULT_LENGTH, pre
*/
this.run = function () {
validate({length});
return `${prefix}${generateCoupon()}${suffix}`;
return generateCoupon();
};
};

Expand Down
6 changes: 4 additions & 2 deletions app/option.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
const {DEFAULT_LENGTH, DEFAULT_PREFIX, DEFAULT_SUFFIX, ALPHABET_UPPERCASE} = require('./constants.js');
const {DEFAULT_LENGTH, DEFAULT_PREFIX, DEFAULT_SUFFIX, ALPHABET_UPPERCASE, CHARSET_ALPHA} = require('./constants.js');

module.exports = {
length: DEFAULT_LENGTH,
prefix: DEFAULT_PREFIX,
suffix: DEFAULT_SUFFIX,
characters: ALPHABET_UPPERCASE
characterSet: {
builtIn: [CHARSET_ALPHA]
}
};
5 changes: 2 additions & 3 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,15 @@ const randomInteger = require('./app/random-integer.js');
* @constructor
*/
const Coupon = function () {

/**
* This will generate coupons.
*
* @param {object} option This is the configuration option.
* @returns {string}
*/
this.generate = function (option) {
const {length, characters, prefix, suffix} = Object.assign({}, defaultOptions, option);
const engine = new Engine(characters, randomInteger, length, prefix, suffix);
const {length, characterSet, prefix, suffix} = Object.assign({}, defaultOptions, option);
const engine = new Engine(characterSet, randomInteger, length, prefix, suffix);
return engine.run();
};
};
Expand Down
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "couponjs",
"version": "0.3.0",
"version": "0.4.0",
"description": "This is a simple coupon creation project using NodeJS.",
"main": "index.js",
"scripts": {
Expand Down
66 changes: 66 additions & 0 deletions tests/app/character-set-builder.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
const {ALPHABET_UPPERCASE, ALPHABET_LOWERCASE, DIGIT, CHARSET_DIGIT, CHARSET_ALPHA_LOWER, CHARSET_ALPHA} = require('../../app/constants.js');
const characterSetBuilder = require('../../app/character-set-builder.js');
const defaultOptions = require('../../app/option.js');

test('Should throw error if invalid builtIn option provided', () => {
expect(() => {
const option = {
builtIn: ['UNKNOWN']
};
const chars = characterSetBuilder(option);
throw new Error('Should have failed.');
}).toThrow('Invalid builtIn characterSet specified. Allowed values ["CHARSET_ALPHA", "CHARSET_ALPHA_LOWER", "CHARSET_DIGIT"]');
});

test('Should return uppercase alphabet A-Z when using default options', () => {
expect(characterSetBuilder(defaultOptions.characterSet)).toBe(ALPHABET_UPPERCASE);
});

test('Should return uppercase alphabet A-Z when using builtIn "CHARSET_ALPHA" option', () => {
expect(characterSetBuilder({builtIn: [CHARSET_ALPHA]})).toBe(ALPHABET_UPPERCASE);
});

test('Should return lowercase alphabet a-z when using builtIn "CHARSET_ALPHA_LOWER" option', () => {
expect(characterSetBuilder({builtIn: [CHARSET_ALPHA_LOWER]})).toBe(ALPHABET_LOWERCASE);
});

test('Should return digits 0-9 when using builtIn "CHARSET_DIGIT" option', () => {
expect(characterSetBuilder({builtIn: [CHARSET_DIGIT]})).toBe(DIGIT);
});

test('Should return uppercase, lowercase alphabet and digits when using builtIn ["CHARSET_ALPHA", "CHARSET_ALPHA_LOWER", "CHARSET_DIGIT"] option', () => {
expect(characterSetBuilder({builtIn: [CHARSET_ALPHA, CHARSET_ALPHA_LOWER, CHARSET_DIGIT]})).toBe(`${ALPHABET_UPPERCASE}${ALPHABET_LOWERCASE}${DIGIT}`);
});

test('Should return alphabet ABC when using custom "ABC" option', () => {
expect(characterSetBuilder({custom: ['ABC']})).toBe('ABC');
});

test('Should return alphabet abc when using custom "abc" option', () => {
expect(characterSetBuilder({custom: ['abc']})).toBe('abc');
});

test('Should return digits 123 when using custom "123" option', () => {
expect(characterSetBuilder({custom: ['123']})).toBe('123');
});

test('Should return alphabet uppercase, lowercase and digit "ABCabc123" when using custom ["ABC", "abc", "123"] option', () => {
expect(characterSetBuilder({custom: ['ABC', 'abc', '123']})).toBe('ABCabc123');
});

test('Should return both builtIn and custom characters when using custom both options', () => {
const option = {
builtIn: [CHARSET_ALPHA, CHARSET_ALPHA_LOWER, CHARSET_DIGIT],
custom: ['@$%']
};
const expectedResult = `${ALPHABET_UPPERCASE}${ALPHABET_LOWERCASE}${DIGIT}@$%`;
expect(characterSetBuilder(option)).toBe(expectedResult);
});

test('Should return unique characters when option has duplicate characters', () => {
const option = {
builtIn: [CHARSET_ALPHA, CHARSET_ALPHA_LOWER, CHARSET_DIGIT],
custom: ['ABC', 'abc', 'BCD', 'xyz', '123']
};
expect(characterSetBuilder(option)).toBe(`${ALPHABET_UPPERCASE}${ALPHABET_LOWERCASE}${DIGIT}`);
});
Loading

0 comments on commit 6c4e5ba

Please sign in to comment.