Skip to content

Commit

Permalink
Merge pull request #24 from yusufshakeel/dev
Browse files Browse the repository at this point in the history
v0.8.11
  • Loading branch information
yusufshakeel committed Oct 10, 2020
2 parents 81293a8 + eb09006 commit 3c3b4e5
Show file tree
Hide file tree
Showing 11 changed files with 178 additions and 43 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,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.8.10-blue.svg)](https://www.npmjs.com/package/couponjs)
[![npm version](https://img.shields.io/badge/npm-0.8.11-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)
[![Coverage Status](https://coveralls.io/repos/github/yusufshakeel/couponjs/badge.svg?branch=master)](https://coveralls.io/github/yusufshakeel/couponjs?branch=master)

Expand Down
10 changes: 4 additions & 6 deletions app/formatter.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,15 @@

const { ERROR_CONSTANTS } = require('./constants.js');
const ValidationError = require('./error/validation-error.js');

const { isUndefined, isString, isObject } = require('./validator/validator.js');
const {
validateFormatRuleString,
validateFormatRuleObject,
hasEqualSumOfGroupsAndCouponLength
} = require('./validator/formatter-validator.js');

function validate(format) {
const formatType = typeof format;

if (formatType === 'undefined') {
if (isUndefined(format)) {
const message = 'Format rule is not specified.';
throw new ValidationError({
message,
Expand All @@ -24,15 +22,15 @@ function validate(format) {
}
]
});
} else if (formatType === 'string') {
} else if (isString(format)) {
const result = validateFormatRuleString(format);
const { groups, totalCharactersInGroup, separators } = result;
return {
groups,
totalCharactersInGroup,
separators
};
} else if (formatType === 'object') {
} else if (isObject(format)) {
const result = validateFormatRuleObject(format);
const { groups, totalCharactersInGroup, separators } = result;
return {
Expand Down
5 changes: 5 additions & 0 deletions app/functional/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
'use strict';

const sumOf = values => values.reduce((sum, size) => sum + size, 0);

module.exports = { sumOf };
9 changes: 4 additions & 5 deletions app/validator/coupon-config-validator.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,12 @@

const { ERROR_CONSTANTS } = require('../constants.js');
const ValidationError = require('../error/validation-error.js');

const isOfType = (variable, type) => typeof variable === type;
const { isUndefined, isInteger, isBoolean } = require('../validator/validator.js');

function couponConfigValidator(config) {
const { verbose, logPerformance, maxNumberOfCouponsToGenerate } = config;

if (verbose && !isOfType(verbose, 'boolean')) {
if (!isUndefined(verbose) && !isBoolean(verbose)) {
throw new ValidationError({
message: `Coupon engine configuration field 'verbose' must be of type boolean.`,
errors: [
Expand All @@ -21,7 +20,7 @@ function couponConfigValidator(config) {
});
}

if (logPerformance && !isOfType(logPerformance, 'boolean')) {
if (!isUndefined(logPerformance) && !isBoolean(logPerformance)) {
throw new ValidationError({
message: `Coupon engine configuration field 'logPerformance' must be of type boolean.`,
errors: [
Expand All @@ -34,7 +33,7 @@ function couponConfigValidator(config) {
});
}

if (maxNumberOfCouponsToGenerate && !Number.isInteger(maxNumberOfCouponsToGenerate)) {
if (!isUndefined(maxNumberOfCouponsToGenerate) && !isInteger(maxNumberOfCouponsToGenerate)) {
throw new ValidationError({
message: `Coupon engine configuration field 'maxNumberOfCouponsToGenerate' must be of type integer.`,
errors: [
Expand Down
38 changes: 18 additions & 20 deletions app/validator/formatter-validator.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,12 @@

const { ERROR_CONSTANTS } = require('../constants.js');
const ValidationError = require('../error/validation-error.js');

const sumOfGroupsCharacters = groups => {
return groups.reduce((sum, size) => sum + size, 0);
};
const { isArray, isString, isInteger, isEmptyArray } = require('../validator/validator.js');
const { sumOf } = require('../functional');

const getErrorsInGroups = groups => {
return groups.reduce((error, group, index) => {
if (typeof group === 'number' && Number.isInteger(group)) {
if (isInteger(group)) {
return error;
}
return [
Expand All @@ -25,17 +23,17 @@ const getErrorsInGroups = groups => {

const getErrorsInSeparators = separators => {
return separators.reduce((error, separator, index) => {
if (typeof separator !== 'string') {
return [
...error,
{
field: 'separators',
message: `Format object must only have string elements in 'separators' array. Found error at index ${index}.`,
type: ERROR_CONSTANTS.COUPONJS_FORMAT_ERROR.type
}
];
if (isString(separator)) {
return error;
}
return error;
return [
...error,
{
field: 'separators',
message: `Format object must only have string elements in 'separators' array. Found error at index ${index}.`,
type: ERROR_CONSTANTS.COUPONJS_FORMAT_ERROR.type
}
];
}, []);
};

Expand All @@ -48,7 +46,7 @@ function validateFormatRuleString(ruleString) {
const isValidFormatRuleString = /^([x]+-?[x]*)*?x$/g.test(ruleString);
if (isValidFormatRuleString) {
const groups = ruleString.split('-').map(group => group.length);
const totalCharactersInGroup = sumOfGroupsCharacters(groups);
const totalCharactersInGroup = sumOf(groups);
const separators = '-'.repeat(groups.length - 1).split('');
return {
groups,
Expand Down Expand Up @@ -77,7 +75,7 @@ function validateFormatRuleString(ruleString) {
function validateFormatRuleObject(ruleObject) {
const { separators, groups } = ruleObject;

if (!Array.isArray(separators)) {
if (!isArray(separators)) {
const message = `Format object must have field 'separators' of type array.`;
throw new ValidationError({
message,
Expand All @@ -91,7 +89,7 @@ function validateFormatRuleObject(ruleObject) {
});
}

if (!Array.isArray(groups)) {
if (!isArray(groups)) {
const message = `Format object must have field 'groups' of type array.`;
throw new ValidationError({
message,
Expand All @@ -105,7 +103,7 @@ function validateFormatRuleObject(ruleObject) {
});
}

if (groups.length === 0) {
if (isEmptyArray(groups)) {
const message = `Format object must have at least one element in the array field 'groups'.`;
throw new ValidationError({
message,
Expand Down Expand Up @@ -172,7 +170,7 @@ function validateFormatRuleObject(ruleObject) {
return {
separators,
groups: groups.map(group => parseInt(group)),
totalCharactersInGroup: sumOfGroupsCharacters(groups)
totalCharactersInGroup: sumOf(groups)
};
}

Expand Down
24 changes: 15 additions & 9 deletions app/validator/generate-coupon-config-validator.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,13 @@ const {
ERROR_CONSTANTS
} = require('../constants.js');
const ValidationError = require('../error/validation-error.js');
const isOfType = (variable, type) => typeof variable === type;
const {
isArray,
isUndefined,
isInteger,
isString,
isEmptyArray
} = require('../validator/validator.js');

const throwValidationError = ({ message, field }) => {
throw new ValidationError({
Expand All @@ -23,8 +29,8 @@ const throwValidationError = ({ message, field }) => {
};

function validateLength(length) {
if (!isOfType(length, 'undefined')) {
if (!Number.isInteger(length)) {
if (!isUndefined(length)) {
if (!isInteger(length)) {
throwValidationError({
message: `The field 'length' must be of type integer.`,
field: 'length'
Expand Down Expand Up @@ -56,8 +62,8 @@ function validateNumberOfCoupons(
maxNumberOfCouponsToGenerate,
totalNumberOfPossibleCoupons
) {
if (!isOfType(numberOfCoupons, 'undefined')) {
if (!Number.isInteger(numberOfCoupons)) {
if (!isUndefined(numberOfCoupons)) {
if (!isInteger(numberOfCoupons)) {
throwValidationError({
message: `The field 'numberOfCoupons' must be of type integer.`,
field: 'numberOfCoupons'
Expand Down Expand Up @@ -91,16 +97,16 @@ function validateNumberOfCoupons(
}

function validateOmitCharacters(omitCharacters) {
if (!isOfType(omitCharacters, 'undefined')) {
if (!Array.isArray(omitCharacters)) {
if (!isUndefined(omitCharacters)) {
if (!isArray(omitCharacters)) {
throwValidationError({
message: `The field 'omitCharacters' must be of type array.`,
field: 'omitCharacters'
});
}

const errors = omitCharacters.reduce((error, omitCharacter, index) => {
if (isOfType(omitCharacter, 'string')) {
if (isString(omitCharacter)) {
return error;
}
return [
Expand All @@ -112,7 +118,7 @@ function validateOmitCharacters(omitCharacters) {
}
];
}, []);
if (errors.length > 0) {
if (!isEmptyArray(errors)) {
throw new ValidationError({
errors,
message: `The field 'omitCharacters' must be an array of strings.`
Expand Down
21 changes: 21 additions & 0 deletions app/validator/validator.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
'use strict';

const isOfType = (operand, type) => typeof operand === type;
const isBoolean = value => typeof value === 'boolean';
const isObject = value => typeof value === 'object';
const isString = value => typeof value === 'string';
const isUndefined = value => typeof value === 'undefined';
const isInteger = value => Number.isInteger(value) && Number.isFinite(value) && `${value}`;
const isArray = value => Array.isArray(value);
const isEmptyArray = value => isArray(value) && value.length === 0;

module.exports = {
isOfType,
isUndefined,
isInteger,
isArray,
isBoolean,
isString,
isObject,
isEmptyArray
};
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.8.10",
"version": "0.8.11",
"description": "This is a simple coupon creation project using NodeJS.",
"main": "index.js",
"scripts": {
Expand Down
7 changes: 7 additions & 0 deletions tests/unit/app/functional/index.unit.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
'use strict';

const { sumOf } = require('../../../../app/functional');

test('Should be able to sum up', () => {
expect(sumOf([1, 2, 3])).toBe(6);
});
101 changes: 101 additions & 0 deletions tests/unit/app/validator/validator.unit.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
'use strict';

const {
isOfType,
isUndefined,
isInteger,
isArray,
isBoolean,
isString,
isObject,
isEmptyArray
} = require('../../../../app/validator/validator.js');

test('Should be able to check the type of the operand', () => {
const foo = { bar: 'foobar' };
expect(isOfType('hello', 'string')).toBeTruthy();
expect(isOfType(123, 'number')).toBeTruthy();
expect(isOfType(foo.bar, 'string')).toBeTruthy();

expect(isOfType('123', 'number')).toBeFalsy();
expect(isOfType(foo.unknown, 'string')).toBeFalsy();
});

test('Should be able to determine that an value is undefined', () => {
const foo = { bar: 'foobar' };
expect(isUndefined(foo.bar)).toBeFalsy();
expect(isUndefined(foo.unknown)).toBeTruthy();
});

test('Should be able to determine that value is integer', () => {
const foo = { bar: 123 };
expect(isInteger(0)).toBeTruthy();
expect(isInteger(123)).toBeTruthy();
expect(isInteger(1e14)).toBeTruthy();
expect(isInteger(-1e14)).toBeTruthy();
expect(isInteger(foo.bar)).toBeTruthy();

expect(isInteger('123')).toBeFalsy();
expect(isInteger('123.0')).toBeFalsy();
expect(isInteger(12.3)).toBeFalsy();
expect(isInteger(Number.POSITIVE_INFINITY)).toBeFalsy();
expect(isInteger(Number.NEGATIVE_INFINITY)).toBeFalsy();
expect(isInteger(foo.unknown)).toBeFalsy();
});

test('Should be able to determine that value is an array', () => {
expect(isArray([])).toBeTruthy();
expect(isArray(['a'])).toBeTruthy();
expect(isArray([1])).toBeTruthy();

expect(isArray(1)).toBeFalsy();
expect(isArray('1')).toBeFalsy();
expect(isArray({})).toBeFalsy();
expect(isArray(null)).toBeFalsy();
expect(isArray(undefined)).toBeFalsy();
});

test('Should be able to determine that value is boolean', () => {
expect(isBoolean(true)).toBeTruthy();
expect(isBoolean(false)).toBeTruthy();

expect(isBoolean('true')).toBeFalsy();
expect(isBoolean('false')).toBeFalsy();
expect(isBoolean(1)).toBeFalsy();
expect(isBoolean(0)).toBeFalsy();
expect(isBoolean({})).toBeFalsy();
expect(isBoolean([])).toBeFalsy();
expect(isBoolean(null)).toBeFalsy();
expect(isBoolean(undefined)).toBeFalsy();
});

test('Should be able to determine that value is string', () => {
expect(isString('')).toBeTruthy();
expect(isString('hello')).toBeTruthy();

expect(isString(true)).toBeFalsy();
expect(isString(false)).toBeFalsy();
expect(isString(1)).toBeFalsy();
expect(isString(0)).toBeFalsy();
expect(isString({})).toBeFalsy();
expect(isString([])).toBeFalsy();
expect(isString(null)).toBeFalsy();
expect(isString(undefined)).toBeFalsy();
});

test('Should be able to determine that value is an empty array', () => {
expect(isEmptyArray([])).toBeTruthy();

expect(isEmptyArray([1])).toBeFalsy();
});

test('Should be able to determine that value is object', () => {
expect(isObject({})).toBeTruthy();
expect(isObject({ foo: 'bar' })).toBeTruthy();

expect(isObject(true)).toBeFalsy();
expect(isObject(false)).toBeFalsy();
expect(isObject(1)).toBeFalsy();
expect(isObject(0)).toBeFalsy();
expect(isObject(undefined)).toBeFalsy();
});

0 comments on commit 3c3b4e5

Please sign in to comment.