├── lib ├── rules │ ├── ipv4.js │ ├── ipv6.js │ ├── activeUrl.js │ ├── timezone.js │ ├── afterOrEqual.js │ ├── beforeOrEqual.js │ ├── after.js │ ├── before.js │ ├── array.js │ ├── object.js │ ├── notIn.js │ ├── domain.js │ ├── hex.js │ ├── integer.js │ ├── decimal.js │ ├── hash.js │ ├── hexColor.js │ ├── iso8601.js │ ├── mongoId.js │ ├── numeric.js │ ├── equals.js │ ├── notContains.js │ ├── macAddress.js │ ├── string.js │ ├── in.js │ ├── requiredNotIf.js │ ├── alphaDash.js │ ├── phoneNumber.js │ ├── arrayUnique.js │ ├── ip.js │ ├── ascii.js │ ├── email.js │ ├── json.js │ ├── base64.js │ ├── latLong.js │ ├── required.js │ ├── url.js │ ├── alpha.js │ ├── creditCard.js │ ├── dateiso.js │ ├── datetime.js │ ├── alphaNumeric.js │ ├── contains.js │ ├── regex.js │ ├── confirmed.js │ ├── date.js │ ├── dateFormat.js │ ├── different.js │ ├── sometimes.js │ ├── boolean.js │ ├── accepted.js │ ├── minLength.js │ ├── maxLength.js │ ├── max.js │ ├── min.js │ ├── length.js │ ├── lt.js │ ├── lte.js │ ├── arrayUniqueObjects.js │ ├── dateAfter.js │ ├── dateBefore.js │ ├── gt.js │ ├── gte.js │ ├── digits.js │ ├── dateBeforeToday.js │ ├── dateAfterToday.js │ ├── same.js │ ├── requiredWith.js │ ├── requiredWithout.js │ ├── digitsBetween.js │ ├── between.js │ ├── lengthBetween.js │ ├── requiredIf.js │ ├── acceptedIf.js │ ├── acceptedNotIf.js │ ├── size.js │ ├── dimensions.js │ ├── mime.js │ └── index.js ├── filters │ ├── index.js │ └── trim.js ├── postRules │ ├── index.js │ ├── all.js │ └── any.js ├── messages │ └── index.js ├── util │ ├── date.js │ ├── namedArgs.js │ ├── empty.js │ ├── ObjectIndex.js │ ├── str.js │ ├── sizeToBytes.js │ ├── obj.js │ └── messageParser.js └── index.js ├── test ├── inputs.js ├── stubs │ ├── file-big.jpg │ └── file-small.png ├── assert.js ├── postRules │ ├── all.js │ ├── any.js │ └── function.js ├── rules │ ├── url.js │ ├── regex.js │ ├── json.js │ ├── mongoId.js │ ├── email.js │ ├── iso1801.js │ ├── latLong.js │ ├── ip.js │ ├── dateBefore.js │ ├── equals.js │ ├── hex.js │ ├── datetime.js │ ├── macAddress.js │ ├── ascii.js │ ├── contains.js │ ├── notContains.js │ ├── hash.js │ ├── nullable.js │ ├── notIn.js │ ├── base64.js │ ├── phoneNumber.js │ ├── different.js │ ├── same.js │ ├── accepted.js │ ├── array.js │ ├── object.js │ ├── in.js │ ├── date.js │ ├── dateAfter.js │ ├── dateFormat.js │ ├── dateBeforeToday.js │ ├── minLength.js │ ├── maxLength.js │ ├── integer.js │ ├── numeric.js │ ├── domain.js │ ├── lt.js │ ├── gt.js │ ├── hexColor.js │ ├── dateiso.js │ ├── arrayUnique.js │ ├── max.js │ ├── dateAfterToday.js │ ├── requiredWithout.js │ ├── decimal.js │ ├── creditCard.js │ ├── alpha.js │ ├── boolean.js │ ├── acceptedNotIf.js │ ├── alphaNumeric.js │ ├── min.js │ ├── lte.js │ ├── gte.js │ ├── requiredNotIf.js │ ├── acceptedIf.js │ ├── length.js │ ├── digits.js │ ├── string.js │ ├── arrayUniqueObjects.js │ ├── required.js │ ├── mime.js │ ├── sometimes.js │ ├── alphaDash.js │ ├── size.js │ ├── lengthBetween.js │ ├── digitsBetween.js │ ├── requiredWith.js │ ├── dimensions.js │ └── between.js ├── async.js ├── non-bailable.js ├── edge.js ├── arrayRules.js ├── rootArray.js ├── util.js └── objects.js ├── jsconfig.json ├── .gitignore ├── .travis.yml ├── stubs └── niv.mjs ├── .github └── workflows │ └── test.yml ├── .eslintrc.json ├── index.d.ts ├── package.json └── CHANGELOG.md /lib/rules/ipv4.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/rules/ipv6.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/inputs.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/rules/activeUrl.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/rules/timezone.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/rules/afterOrEqual.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/rules/beforeOrEqual.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/rules/after.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./dateAfter'); 2 | -------------------------------------------------------------------------------- /lib/filters/index.js: -------------------------------------------------------------------------------- 1 | module.exports.trim = require('./trim'); 2 | -------------------------------------------------------------------------------- /lib/rules/before.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./dateBefore'); 2 | -------------------------------------------------------------------------------- /lib/postRules/index.js: -------------------------------------------------------------------------------- 1 | exports.all = require('./all'); 2 | exports.any = require('./any'); 3 | -------------------------------------------------------------------------------- /lib/rules/array.js: -------------------------------------------------------------------------------- 1 | module.exports = function array({ value }) { 2 | return Array.isArray(value); 3 | }; 4 | -------------------------------------------------------------------------------- /test/stubs/file-big.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitnbytesio/node-input-validator/HEAD/test/stubs/file-big.jpg -------------------------------------------------------------------------------- /test/stubs/file-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitnbytesio/node-input-validator/HEAD/test/stubs/file-small.png -------------------------------------------------------------------------------- /lib/rules/object.js: -------------------------------------------------------------------------------- 1 | module.exports = function object({ value }) { 2 | return (!!value) && (value.constructor === Object); 3 | }; 4 | -------------------------------------------------------------------------------- /lib/rules/notIn.js: -------------------------------------------------------------------------------- 1 | const inRule = require('./in'); 2 | 3 | module.exports = function notIn(...args) { 4 | return !inRule(...args); 5 | }; 6 | -------------------------------------------------------------------------------- /lib/rules/domain.js: -------------------------------------------------------------------------------- 1 | const v = require('validator'); 2 | 3 | module.exports = function domain({ value }) { 4 | return v.isFQDN(String(value)); 5 | }; 6 | -------------------------------------------------------------------------------- /lib/rules/hex.js: -------------------------------------------------------------------------------- 1 | const v = require('validator'); 2 | 3 | module.exports = function hex({ value }) { 4 | return v.isHexadecimal(String(value)); 5 | }; 6 | -------------------------------------------------------------------------------- /lib/rules/integer.js: -------------------------------------------------------------------------------- 1 | const v = require('validator'); 2 | 3 | module.exports = function integer({ value }) { 4 | return v.isInt(String(value)); 5 | }; 6 | -------------------------------------------------------------------------------- /lib/rules/decimal.js: -------------------------------------------------------------------------------- 1 | const v = require('validator'); 2 | 3 | module.exports = function decimal({ value }) { 4 | return v.isDecimal(String(value)); 5 | }; 6 | -------------------------------------------------------------------------------- /lib/rules/hash.js: -------------------------------------------------------------------------------- 1 | const v = require('validator'); 2 | 3 | module.exports = function hash({ value, args }) { 4 | return v.isHash(String(value), args); 5 | }; 6 | -------------------------------------------------------------------------------- /lib/rules/hexColor.js: -------------------------------------------------------------------------------- 1 | const v = require('validator'); 2 | 3 | module.exports = function hexColor({ value }) { 4 | return v.isHexColor(String(value)); 5 | }; 6 | -------------------------------------------------------------------------------- /lib/rules/iso8601.js: -------------------------------------------------------------------------------- 1 | const v = require('validator'); 2 | 3 | module.exports = function iso8601({ value }) { 4 | return v.isISO8601(String(value)); 5 | }; 6 | -------------------------------------------------------------------------------- /lib/rules/mongoId.js: -------------------------------------------------------------------------------- 1 | const v = require('validator'); 2 | 3 | module.exports = function mongoId({ value }) { 4 | return v.isMongoId(String(value)); 5 | }; 6 | -------------------------------------------------------------------------------- /lib/rules/numeric.js: -------------------------------------------------------------------------------- 1 | const v = require('validator'); 2 | 3 | module.exports = function numeric({ value }) { 4 | return v.isNumeric(String(value)); 5 | }; 6 | -------------------------------------------------------------------------------- /lib/rules/equals.js: -------------------------------------------------------------------------------- 1 | module.exports = function equals({ value, args }) { 2 | if (value !== args[0]) { 3 | return false; 4 | } 5 | 6 | return true; 7 | }; 8 | -------------------------------------------------------------------------------- /lib/rules/notContains.js: -------------------------------------------------------------------------------- 1 | const contains = require('./contains'); 2 | 3 | module.exports = function notContains(...args) { 4 | return !contains(...args); 5 | }; 6 | -------------------------------------------------------------------------------- /lib/rules/macAddress.js: -------------------------------------------------------------------------------- 1 | const v = require('validator'); 2 | 3 | module.exports = function macAddress({ value }) { 4 | return v.isMACAddress(String(value)); 5 | }; 6 | -------------------------------------------------------------------------------- /lib/rules/string.js: -------------------------------------------------------------------------------- 1 | module.exports = function string({ value }) { 2 | if (typeof value !== 'string') { 3 | return false; 4 | } 5 | 6 | return true; 7 | }; 8 | -------------------------------------------------------------------------------- /lib/rules/in.js: -------------------------------------------------------------------------------- 1 | module.exports = function _in({ value, args }) { 2 | if (args.indexOf(String(value)) < 0) { 3 | return false; 4 | } 5 | 6 | return true; 7 | }; 8 | -------------------------------------------------------------------------------- /lib/rules/requiredNotIf.js: -------------------------------------------------------------------------------- 1 | const requiredIf = require('./requiredIf'); 2 | 3 | module.exports = function requiredNotIf(...args) { 4 | return !requiredIf(...args); 5 | }; 6 | -------------------------------------------------------------------------------- /lib/rules/alphaDash.js: -------------------------------------------------------------------------------- 1 | module.exports = function alphaDash({ value }) { 2 | if (!(/^[A-Z0-9_-]+$/i.test(value))) { 3 | return false; 4 | } 5 | 6 | return true; 7 | }; 8 | -------------------------------------------------------------------------------- /lib/rules/phoneNumber.js: -------------------------------------------------------------------------------- 1 | const v = require('validator'); 2 | 3 | module.exports = function phoneNumber({ value, args }) { 4 | // @ts-ignore 5 | return v.isMobilePhone(String(value), ...args); 6 | }; 7 | -------------------------------------------------------------------------------- /lib/rules/arrayUnique.js: -------------------------------------------------------------------------------- 1 | module.exports = function arrayUnique({ value }) { 2 | if (!Array.isArray(value)) { 3 | return false; 4 | } 5 | 6 | return (new Set(value)).size === value.length; 7 | }; 8 | -------------------------------------------------------------------------------- /lib/rules/ip.js: -------------------------------------------------------------------------------- 1 | const v = require('validator'); 2 | 3 | module.exports = function ip({ value }) { 4 | if (!v.isIP(String(value))) { 5 | return false; 6 | } 7 | 8 | return true; 9 | }; 10 | -------------------------------------------------------------------------------- /lib/rules/ascii.js: -------------------------------------------------------------------------------- 1 | const v = require('validator'); 2 | 3 | module.exports = function ascii({ value }) { 4 | if (v.isAscii(String(value))) { 5 | return true; 6 | } 7 | 8 | return false; 9 | }; 10 | -------------------------------------------------------------------------------- /lib/rules/email.js: -------------------------------------------------------------------------------- 1 | const v = require('validator'); 2 | 3 | module.exports = function email({ value }) { 4 | if (!v.isEmail(String(value))) { 5 | return false; 6 | } 7 | 8 | return true; 9 | }; 10 | -------------------------------------------------------------------------------- /lib/rules/json.js: -------------------------------------------------------------------------------- 1 | const v = require('validator'); 2 | 3 | module.exports = function json({ value }) { 4 | if (!v.isJSON(String(value))) { 5 | return false; 6 | } 7 | 8 | return true; 9 | }; 10 | -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES6", 4 | "checkJs": true 5 | }, 6 | "exclude": [ 7 | "node_modules", 8 | "**/node_modules/*" 9 | ] 10 | } -------------------------------------------------------------------------------- /lib/rules/base64.js: -------------------------------------------------------------------------------- 1 | const v = require('validator'); 2 | 3 | module.exports = function base64({ value }) { 4 | if (v.isBase64(String(value))) { 5 | return true; 6 | } 7 | 8 | return false; 9 | }; 10 | -------------------------------------------------------------------------------- /lib/rules/latLong.js: -------------------------------------------------------------------------------- 1 | const v = require('validator'); 2 | 3 | module.exports = function latLong({ value }) { 4 | if (v.isLatLong(String(value))) { 5 | return true; 6 | } 7 | 8 | return false; 9 | }; 10 | -------------------------------------------------------------------------------- /lib/rules/required.js: -------------------------------------------------------------------------------- 1 | const empty = require('../util/empty'); 2 | 3 | module.exports = function required({ value }) { 4 | if (empty.reallyEmptyTrimmed(value)) { 5 | return false; 6 | } 7 | return true; 8 | }; 9 | -------------------------------------------------------------------------------- /lib/rules/url.js: -------------------------------------------------------------------------------- 1 | const v = require('validator'); 2 | 3 | module.exports = function url({ value }) { 4 | if (typeof value !== 'string') { 5 | return false; 6 | } 7 | 8 | return v.isURL(value); 9 | }; 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | #ide 2 | node_modules 3 | .idea 4 | .vscode 5 | 6 | # others 7 | test.js 8 | coverage 9 | npm-debug.log 10 | .coveralls.yml 11 | *.log 12 | *.0x 13 | .nyc_output 14 | docs 15 | cjs 16 | esm 17 | doctheme 18 | -------------------------------------------------------------------------------- /lib/rules/alpha.js: -------------------------------------------------------------------------------- 1 | const v = require('validator'); 2 | 3 | module.exports = function alpha({ value, args }) { 4 | if (v.isAlpha(String(value), ...args)) { 5 | return true; 6 | } 7 | 8 | return false; 9 | }; 10 | -------------------------------------------------------------------------------- /lib/rules/creditCard.js: -------------------------------------------------------------------------------- 1 | const v = require('validator'); 2 | 3 | module.exports = function creditCard({ value }) { 4 | if (v.isCreditCard(String(value))) { 5 | return true; 6 | } 7 | 8 | return false; 9 | }; 10 | -------------------------------------------------------------------------------- /lib/rules/dateiso.js: -------------------------------------------------------------------------------- 1 | const moment = require('moment'); 2 | 3 | module.exports = function date({ value }) { 4 | if (!moment(value, moment.ISO_8601).isValid()) { 5 | return false; 6 | } 7 | 8 | return true; 9 | }; 10 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "12" 4 | - "14" 5 | - "16" 6 | - "18" 7 | install: 8 | - npm install 9 | script: "npm test" 10 | after_success: 11 | - npm run coverage 12 | - npm run report-coverage 13 | -------------------------------------------------------------------------------- /lib/filters/trim.js: -------------------------------------------------------------------------------- 1 | /** 2 | * trim filter 3 | * @param {string} value 4 | * @return {Promise.} 5 | */ 6 | module.exports = function trim(value) { /* istanbul ignore next */ 7 | return Promise.resolve(value.trim()); 8 | }; 9 | -------------------------------------------------------------------------------- /lib/rules/datetime.js: -------------------------------------------------------------------------------- 1 | const moment = require('moment'); 2 | 3 | module.exports = function date({ value }) { 4 | if (!moment(value, 'YYYY-MM-DD HH:mm:ss', true).isValid()) { 5 | return false; 6 | } 7 | 8 | return true; 9 | }; 10 | -------------------------------------------------------------------------------- /lib/rules/alphaNumeric.js: -------------------------------------------------------------------------------- 1 | const v = require('validator'); 2 | 3 | module.exports = function alphaNumeric({ value, args }) { 4 | if (!v.isAlphanumeric(String(value), ...args)) { 5 | return false; 6 | } 7 | 8 | return true; 9 | }; 10 | -------------------------------------------------------------------------------- /lib/rules/contains.js: -------------------------------------------------------------------------------- 1 | const v = require('validator'); 2 | 3 | module.exports = function contains({ value, args }) { 4 | const [find] = args; 5 | if (!v.contains(String(value), find)) { 6 | return false; 7 | } 8 | 9 | return true; 10 | }; 11 | -------------------------------------------------------------------------------- /lib/rules/regex.js: -------------------------------------------------------------------------------- 1 | module.exports = function regex({ value, args }) { 2 | const [pattren, flags] = args; 3 | const regexp = new RegExp(pattren, flags); 4 | 5 | if (!regexp.test(value)) { 6 | return false; 7 | } 8 | 9 | return true; 10 | }; 11 | -------------------------------------------------------------------------------- /lib/rules/confirmed.js: -------------------------------------------------------------------------------- 1 | module.exports = ({ value, attr }, v) => { 2 | const otherInput = `${attr}Confirmation`; 3 | 4 | const otherValue = v.inputs[otherInput]; 5 | 6 | if (otherValue === value) { 7 | return true; 8 | } 9 | 10 | return false; 11 | }; 12 | -------------------------------------------------------------------------------- /lib/rules/date.js: -------------------------------------------------------------------------------- 1 | const moment = require('moment'); 2 | 3 | module.exports = function date({ value, args }) { 4 | const [format = 'YYYY-MM-DD'] = args; 5 | if (!moment(value, format, true).isValid()) { 6 | return false; 7 | } 8 | 9 | return true; 10 | }; 11 | -------------------------------------------------------------------------------- /lib/rules/dateFormat.js: -------------------------------------------------------------------------------- 1 | const moment = require('moment'); 2 | 3 | module.exports = function dateFormat({ value, args }) { 4 | const [format] = args; 5 | if (!moment(value, format, true).isValid()) { 6 | return false; 7 | } 8 | 9 | return true; 10 | }; 11 | -------------------------------------------------------------------------------- /lib/rules/different.js: -------------------------------------------------------------------------------- 1 | module.exports = ({ value, args }, v) => { 2 | if (!args.length) { 3 | throw new Error('Invalid number of arguments'); 4 | } 5 | 6 | const [otherInput] = args; 7 | 8 | const otherValue = v.inputs[otherInput]; 9 | 10 | if (otherValue === value) { 11 | return false; 12 | } 13 | 14 | return true; 15 | }; 16 | -------------------------------------------------------------------------------- /lib/rules/sometimes.js: -------------------------------------------------------------------------------- 1 | const _has = require('lodash.has'); 2 | const empty = require('../util/empty'); 3 | 4 | module.exports = function sometimes({ attr, value }, v) { 5 | // @ts-ignore 6 | if (!_has(v.inputs, attr)) { 7 | return true; 8 | } 9 | 10 | if (empty.reallyEmptyTrimmed(value)) { 11 | return false; 12 | } 13 | 14 | return true; 15 | }; 16 | -------------------------------------------------------------------------------- /lib/messages/index.js: -------------------------------------------------------------------------------- 1 | const messages = require('./en/messages'); 2 | const messagesPersian = require('./fa/messages'); 3 | const messagesBrazilian = require('./ptBR/messages'); 4 | const messagesTurkish = require('./tr/messages'); 5 | 6 | module.exports = { en: messages, fa: messagesPersian, ptBR: messagesBrazilian, tr: messagesTurkish }; 7 | 8 | module.exports.defaultLang = 'en'; 9 | -------------------------------------------------------------------------------- /lib/rules/boolean.js: -------------------------------------------------------------------------------- 1 | module.exports = function boolean({ value, args }) { 2 | let opts = [true, false, 'true', 'false', 0, 1, '0', '1']; 3 | 4 | if (args.length) { 5 | opts = args; 6 | } 7 | // throw new Error(`${value}, ${opts.indexOf(value)}, ${attr}`); 8 | 9 | if (opts.indexOf(value) >= 0) { 10 | return true; 11 | } 12 | 13 | return false; 14 | }; 15 | -------------------------------------------------------------------------------- /lib/rules/accepted.js: -------------------------------------------------------------------------------- 1 | module.exports = function accepted({ value, args }) { 2 | // default options 3 | let opts = [true, 'true', 1, '1', 'yes', 'on']; 4 | // in case of custom options 5 | if (args.length) { 6 | opts = args; 7 | } 8 | // if value not found in opts 9 | if (opts.indexOf(value) >= 0) { 10 | return true; 11 | } 12 | 13 | return false; 14 | }; 15 | -------------------------------------------------------------------------------- /lib/rules/minLength.js: -------------------------------------------------------------------------------- 1 | const v = require('validator'); 2 | 3 | module.exports = function minLength({ attr, value, args }) { 4 | const [minNum] = args; 5 | if (!v.isInt(minNum)) { 6 | throw new Error(`Seed in minLength rule for ${attr} must be a number.`); 7 | } 8 | 9 | if (value.toString().length < parseInt(minNum)) { 10 | return false; 11 | } 12 | 13 | return true; 14 | }; 15 | -------------------------------------------------------------------------------- /lib/rules/maxLength.js: -------------------------------------------------------------------------------- 1 | const v = require('validator'); 2 | 3 | module.exports = function maxLength({ attr, value, args }) { 4 | const [maxNum] = args; 5 | if (!v.isInt(maxNum)) { 6 | throw new Error(`Seed in maxLength rule for ${attr} must be a number.`); 7 | } 8 | 9 | if (value && value.toString().length > parseInt(maxNum)) { 10 | return false; 11 | } 12 | 13 | return true; 14 | }; 15 | -------------------------------------------------------------------------------- /lib/util/date.js: -------------------------------------------------------------------------------- 1 | const moment = require('moment'); 2 | 3 | /** 4 | * default supported data formats 5 | */ 6 | const dateFormats = [ 7 | moment.ISO_8601, 8 | 'DD-MM-YYYY', 9 | 'DD.MM.YYYY', 10 | 'DD/MM/YYYY', 11 | 'D-M-YYYY', 12 | 'D.M.YYYY', 13 | 'D/M/YYYY', 14 | 'YYYY-MM-DD HH:mm:Z', 15 | 'YYYY-MM-DD HH:mm:ZZ', 16 | 'YYYY-MM-DD HH:mm Z', 17 | ]; 18 | 19 | module.exports = { dateFormats }; 20 | -------------------------------------------------------------------------------- /lib/rules/max.js: -------------------------------------------------------------------------------- 1 | const numeric = require('./numeric'); 2 | 3 | module.exports = function max({ attr, value, args }) { 4 | const [maxNum] = args; 5 | if (!numeric({ value: maxNum })) { 6 | throw new Error(`Seed in max rule for ${attr} must be a number.`); 7 | } 8 | 9 | if (!numeric({ value: String(value) }) || Number(value) > Number(maxNum)) { 10 | return false; 11 | } 12 | 13 | return true; 14 | }; 15 | -------------------------------------------------------------------------------- /lib/rules/min.js: -------------------------------------------------------------------------------- 1 | const numeric = require('./numeric'); 2 | 3 | module.exports = function min({ attr, value, args }) { 4 | const [minNum] = args; 5 | // throw minNum; 6 | if (!numeric({ value: minNum })) { 7 | throw new Error(`Seed in min rule for ${attr} must be a number.`); 8 | } 9 | 10 | if (!numeric({ value: String(value) }) || Number(value) < Number(minNum)) { 11 | return false; 12 | } 13 | 14 | return true; 15 | }; 16 | -------------------------------------------------------------------------------- /lib/rules/length.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable radix */ 2 | 3 | module.exports = function length({ value, args }) { 4 | let min; 5 | 6 | const max = parseInt(args[0]); 7 | 8 | if (args[1]) { 9 | min = parseInt(args[1]); 10 | } 11 | 12 | const valueLength = value.length; 13 | 14 | if (valueLength <= max) { 15 | if (min && valueLength < min) { 16 | return false; 17 | } 18 | 19 | return true; 20 | } 21 | 22 | return false; 23 | }; 24 | -------------------------------------------------------------------------------- /lib/rules/lt.js: -------------------------------------------------------------------------------- 1 | const { pathIndex } = require('../util/ObjectIndex'); 2 | const numeric = require('./numeric'); 3 | 4 | module.exports = function lt({ value, args }, v) { 5 | const [anotherField] = args; 6 | const anotherFieldValue = pathIndex(v.inputs, anotherField); 7 | 8 | if (!numeric({ value })) { 9 | return false; 10 | } 11 | 12 | if (Number(value) < Number(anotherFieldValue)) { 13 | return true; 14 | } 15 | 16 | return false; 17 | }; 18 | -------------------------------------------------------------------------------- /lib/rules/lte.js: -------------------------------------------------------------------------------- 1 | const { pathIndex } = require('../util/ObjectIndex'); 2 | const numeric = require('./numeric'); 3 | 4 | module.exports = function gte({ value, args }, v) { 5 | const [anotherField] = args; 6 | const anotherFieldValue = pathIndex(v.inputs, anotherField); 7 | 8 | if (!numeric({ value })) { 9 | return false; 10 | } 11 | 12 | if (Number(value) <= Number(anotherFieldValue)) { 13 | return true; 14 | } 15 | 16 | return false; 17 | }; 18 | -------------------------------------------------------------------------------- /lib/util/namedArgs.js: -------------------------------------------------------------------------------- 1 | /** 2 | * parse named args 3 | * @param {Array} params 4 | * @return {Object} 5 | */ 6 | function namedArgs(params) { 7 | const obj = {}; 8 | if (!Array.isArray(params)) { 9 | return obj; 10 | } 11 | 12 | params.forEach((i) => { 13 | const [k, v] = i.split('='); 14 | if (v && v.length) { 15 | obj[k.trim()] = v.trim() || null; 16 | } 17 | }); 18 | 19 | return obj; 20 | } 21 | 22 | module.exports = namedArgs; 23 | -------------------------------------------------------------------------------- /lib/rules/arrayUniqueObjects.js: -------------------------------------------------------------------------------- 1 | module.exports = function arrayUniqueObjects({ value, args }) { 2 | if (!Array.isArray(value)) { 3 | return false; 4 | } 5 | 6 | const result = new Set(value.map((o) => { 7 | let output = ''; 8 | 9 | // eslint-disable-next-line no-restricted-syntax 10 | for (const attr of args) { 11 | output += o[attr]; 12 | } 13 | 14 | return output; 15 | })); 16 | 17 | return result.size === value.length; 18 | }; 19 | -------------------------------------------------------------------------------- /lib/rules/dateAfter.js: -------------------------------------------------------------------------------- 1 | const moment = require('moment'); 2 | const { dateFormats } = require('../util/date'); 3 | 4 | module.exports = function dateAfter({ value, args }) { 5 | const [afterDate] = args; 6 | 7 | const mAfterDate = moment(afterDate, dateFormats); 8 | const mDate = moment(value, dateFormats); 9 | 10 | if (!mAfterDate.isValid() || !mDate.isValid() || mAfterDate.valueOf() > mDate.valueOf()) { 11 | return false; 12 | } 13 | 14 | return true; 15 | }; 16 | -------------------------------------------------------------------------------- /lib/rules/dateBefore.js: -------------------------------------------------------------------------------- 1 | const moment = require('moment'); 2 | const { dateFormats } = require('../util/date'); 3 | 4 | module.exports = function validateBefore({ value, args }) { 5 | const [beforeDate] = args; 6 | const mBeforeDate = moment(beforeDate, dateFormats); 7 | const mDate = moment(value, dateFormats); 8 | 9 | if (!mBeforeDate.isValid() || !mDate.isValid() || mBeforeDate.valueOf() < mDate.valueOf()) { 10 | return false; 11 | } 12 | 13 | return true; 14 | }; 15 | -------------------------------------------------------------------------------- /lib/rules/gt.js: -------------------------------------------------------------------------------- 1 | const { pathIndex } = require('../util/ObjectIndex'); 2 | const numeric = require('./numeric'); 3 | 4 | module.exports = function gt({ value, args }, validator) { 5 | const [anotherField] = args; 6 | const anotherFieldValue = pathIndex(validator.inputs, anotherField); 7 | 8 | if (!numeric({ value })) { 9 | return false; 10 | } 11 | 12 | if (Number(value) > Number(anotherFieldValue)) { 13 | return true; 14 | } 15 | 16 | return false; 17 | }; 18 | -------------------------------------------------------------------------------- /lib/rules/gte.js: -------------------------------------------------------------------------------- 1 | const { pathIndex } = require('../util/ObjectIndex'); 2 | const numeric = require('./numeric'); 3 | 4 | module.exports = function gte({ value, args }, v) { 5 | const [anotherField] = args; 6 | const anotherFieldValue = pathIndex(v.inputs, anotherField); 7 | 8 | if (!numeric({ value: anotherFieldValue })) { 9 | return false; 10 | } 11 | 12 | if (Number(value) >= Number(anotherFieldValue)) { 13 | return true; 14 | } 15 | 16 | return false; 17 | }; 18 | -------------------------------------------------------------------------------- /lib/util/empty.js: -------------------------------------------------------------------------------- 1 | function reallyEmpty(value) { 2 | if (!value && [false, 0].indexOf(value) < 0) { 3 | return true; 4 | } 5 | 6 | return !value.toString(); 7 | }; 8 | 9 | function reallyEmptyTrimmed(value) { 10 | if (!value && [false, 0].indexOf(value) < 0) { 11 | return true; 12 | } 13 | 14 | return !value.toString().trim(); 15 | }; 16 | 17 | module.exports = { 18 | reallyEmpty, 19 | reallyEmptyTrimmed, 20 | alter(prop, value) { 21 | module.exports[prop] = value; 22 | }, 23 | }; 24 | 25 | -------------------------------------------------------------------------------- /lib/rules/digits.js: -------------------------------------------------------------------------------- 1 | const v = require('validator'); 2 | const numeric = require('./numeric'); 3 | 4 | module.exports = function digits({ attr, value, args }) { 5 | const [dNumber] = args; 6 | if (!numeric({ value: dNumber })) { 7 | throw new Error(`Please provide a numeric value for ${attr} under digits rule.`); 8 | } 9 | 10 | if (!v.isInt(String(value))) { 11 | return false; 12 | } 13 | 14 | if (Number(dNumber) !== value.toString().length) { 15 | return false; 16 | } 17 | 18 | return true; 19 | }; 20 | -------------------------------------------------------------------------------- /lib/rules/dateBeforeToday.js: -------------------------------------------------------------------------------- 1 | const moment = require('moment'); 2 | const { dateFormats } = require('../util/date'); 3 | 4 | module.exports = function dateDaysBeforeToday({ value, args }) { 5 | // after date moment object 6 | const mAfterDate = moment().subtract(args[0], args[1] || 'days'); 7 | // input date moment object 8 | const mDate = moment(value, dateFormats); 9 | 10 | if (!mAfterDate.isValid() || !mDate.isValid() || mAfterDate.valueOf() < mDate.valueOf()) { 11 | return false; 12 | } 13 | 14 | return true; 15 | }; 16 | -------------------------------------------------------------------------------- /lib/rules/dateAfterToday.js: -------------------------------------------------------------------------------- 1 | const moment = require('moment'); 2 | const { dateFormats } = require('../util/date'); 3 | 4 | module.exports = function dateDaysAfterToday({ value, args }) { 5 | // after date moment object 6 | const mAfterDate = moment().add(args[0], args[1] || 'days'); 7 | // input date moment object 8 | const mDate = moment(value, dateFormats); 9 | 10 | /* istanbul ignore next */ 11 | if (!mAfterDate.isValid() || !mDate.isValid() || mAfterDate.valueOf() > mDate.valueOf()) { 12 | return false; 13 | } 14 | 15 | return true; 16 | }; 17 | -------------------------------------------------------------------------------- /lib/util/ObjectIndex.js: -------------------------------------------------------------------------------- 1 | // obj,'1.2.3' -> multiIndex(obj,['1','2','3']) 2 | 3 | // obj,['1','2','3'] -> ((obj['1'])['2'])['3'] 4 | /** 5 | * 6 | * @param {*} obj 7 | * @param {*} is 8 | * @return {*} 9 | */ 10 | function multiIndex(obj, is) { 11 | return is.length ? multiIndex(obj[is[0]], is.slice(1)) : obj; 12 | } 13 | 14 | /** 15 | * get path index 16 | * @param {*} obj 17 | * @param {*} is 18 | * @return {*} 19 | */ 20 | function pathIndex(obj, is) { 21 | return multiIndex(obj, is.split('.')); 22 | } 23 | 24 | module.exports = { pathIndex, multiIndex }; 25 | -------------------------------------------------------------------------------- /lib/util/str.js: -------------------------------------------------------------------------------- 1 | exports.camelToSentance = function camelToSentance(str) { 2 | return str.replace(/([A-Z]+)/g, ' $1').trimLeft().toLowerCase(); 3 | }; 4 | 5 | exports.snakeToSentance = function snakeToSentance(str) { 6 | return str.replace(/_/g, ' '); 7 | }; 8 | 9 | exports.trim = function trim(string, char = ' ') { 10 | let str = string; 11 | 12 | if (str.charAt(0) === char) { 13 | str = str.substr(1, str.length); 14 | } 15 | 16 | const len = str.length; 17 | 18 | if (str.charAt(len - 1) === char) { 19 | str = str.substr(0, len - 1); 20 | } 21 | 22 | return str.toString(); 23 | }; 24 | -------------------------------------------------------------------------------- /test/assert.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | const niv = require('../lib/index'); 4 | 5 | describe('Assert Rules', () => { 6 | it('should pass with valid rule', async () => { 7 | niv.assert({ 8 | password: 'required|string', 9 | }); 10 | }); 11 | 12 | it('should throw exception for non existing rule', async () => { 13 | try { 14 | niv.assert({ 15 | password: 'passIt', 16 | }); 17 | 18 | throw new Error('Invalid seed exception.'); 19 | } catch (e) { 20 | assert.equal(e, 'Error: Rule passIt used for attribute password is invalid.'); 21 | } 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /stubs/niv.mjs: -------------------------------------------------------------------------------- 1 | const { Validator } = require('../lib/index.js'); 2 | 3 | const v = new Validator( 4 | { 5 | order: { 6 | price: 100, 7 | items: [ 8 | { id: 1, quantity: 2, _v: 2 }, 9 | // { id: 2, quantity: 4, _v: 2 }, 10 | ], 11 | _v: 1, 12 | }, 13 | extra: 1, 14 | _v: 1, 15 | }, 16 | { 17 | order: 'object', 18 | 'order.price': 'numeric', 19 | 'order.items': 'array', 20 | 'order.items.*.id': 'numeric', 21 | //'order.items.*.quantity': 'numeric', 22 | }, 23 | ); 24 | 25 | v.validate().then(passed => { 26 | console.log('result', passed, v.errors, v.data()); 27 | }); -------------------------------------------------------------------------------- /lib/rules/same.js: -------------------------------------------------------------------------------- 1 | module.exports = function same({ value, args }, v) { 2 | let [otherField] = args; 3 | otherField = otherField.split('.').filter((e) => e !== ''); 4 | 5 | let otherValue; 6 | 7 | // eslint-disable-next-line array-callback-return 8 | otherField.map((item) => { 9 | if (typeof otherValue === 'undefined') { 10 | otherValue = v.inputs && v.inputs[item]; 11 | } else { 12 | otherValue = otherValue[item]; 13 | } 14 | }); 15 | 16 | if (typeof otherValue === 'undefined') { 17 | return false; 18 | } 19 | 20 | if (otherValue !== value) { 21 | return false; 22 | } 23 | 24 | return true; 25 | }; 26 | -------------------------------------------------------------------------------- /test/postRules/all.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | const { Validator } = require('../../lib/index'); 4 | 5 | describe('all', () => { 6 | it('should return true when all fields exists', async () => { 7 | const v = new Validator({ field1: '1', field2: '2', field3: '3' }, { '*': 'all:field1,field2,field3' }); 8 | 9 | const matched = await v.check(); 10 | 11 | assert.equal(matched, true); 12 | }); 13 | 14 | it('should return false when there is one field missing', async () => { 15 | const v = new Validator({ field1: '1', field2: '2' }, { '*': 'all:field1,field2,field3' }); 16 | 17 | const matched = await v.check(); 18 | 19 | assert.equal(matched, false); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /lib/rules/requiredWith.js: -------------------------------------------------------------------------------- 1 | const empty = require('../util/empty'); 2 | const { pathIndex } = require('../util/ObjectIndex'); 3 | 4 | module.exports = function requiredWith({ attr, value, args }, v) { 5 | if (!args.length) { 6 | throw new Error(`Invalid arguments supplied for field ${attr} in required with rule.`); 7 | } 8 | 9 | let i; let required = false; 10 | 11 | for (i = 0; i < args.length; ++i) { 12 | if (args[i] === attr) { 13 | continue; 14 | } 15 | 16 | if (!empty.reallyEmptyTrimmed(pathIndex(v.inputs, args[i]))) { 17 | required = true; 18 | break; 19 | } 20 | } 21 | 22 | if (required && empty.reallyEmptyTrimmed(value)) { 23 | return false; 24 | } 25 | 26 | return true; 27 | }; 28 | -------------------------------------------------------------------------------- /test/postRules/any.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-undef */ 2 | const assert = require('assert'); 3 | 4 | const { Validator } = require('../../lib/index'); 5 | 6 | describe('Post', () => { 7 | describe('any', () => { 8 | it('should return true when at least one field exists', async () => { 9 | const v = new Validator({ field1: '1' }, { '*': 'any:field1,field2,field3' }); 10 | 11 | const matched = await v.check(); 12 | 13 | assert.equal(matched, true); 14 | }); 15 | 16 | it('should return false when there is no fields', async () => { 17 | const v = new Validator({}, { '*': 'any:field1,field2,field3' }); 18 | 19 | const matched = await v.check(); 20 | 21 | assert.equal(matched, false); 22 | }); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /lib/rules/requiredWithout.js: -------------------------------------------------------------------------------- 1 | const empty = require('../util/empty'); 2 | const { pathIndex } = require('../util/ObjectIndex'); 3 | 4 | module.exports = function requiredWithout({ attr, value, args }, v) { 5 | if (!args.length) { 6 | throw new Error(`Invalid arguments supplied for field ${attr} in requiredWithout rule.`); 7 | } 8 | 9 | let i; let required = false; 10 | 11 | for (i = 0; i < args.length; ++i) { 12 | if (args[i] === attr) { 13 | continue; 14 | } 15 | 16 | // @ts-ignore 17 | if (empty.reallyEmptyTrimmed(pathIndex(v.inputs, args[i]))) { 18 | required = true; 19 | break; 20 | } 21 | } 22 | 23 | if (required && empty.reallyEmptyTrimmed(value)) { 24 | return false; 25 | } 26 | 27 | return true; 28 | }; 29 | -------------------------------------------------------------------------------- /lib/postRules/all.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-restricted-syntax */ 2 | /** 3 | * post validation rule all 4 | * @param {*} rule 5 | * @param {*} v 6 | * @return {*} 7 | */ 8 | module.exports = function all({ params }, v) { 9 | const values = v.inputs; 10 | 11 | let result = true; 12 | 13 | for (const k in params) { 14 | if (values[params[k]] === undefined) { 15 | result = false; 16 | break; 17 | } 18 | } 19 | 20 | if (result) { 21 | return true; 22 | } 23 | 24 | params.forEach((field) => { 25 | // const field = params[k]; 26 | const value = v.parseValue(field); 27 | v.error(field, 'required', v.getParsedMessage({ 28 | rule: 'required', 29 | attr: field, 30 | value, 31 | args: params, 32 | })); 33 | }); 34 | 35 | return false; 36 | }; 37 | -------------------------------------------------------------------------------- /lib/postRules/any.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable guard-for-in */ 2 | /* eslint-disable no-restricted-syntax */ 3 | /** 4 | * post validation rule any 5 | * @param {*} rule 6 | * @param {*} v 7 | * @return {*} 8 | */ 9 | module.exports = function any({ params }, v) { 10 | const values = v.inputs; 11 | 12 | for (const k in params) { 13 | const field = params[k]; 14 | 15 | if (values[field]) { 16 | return true; 17 | } 18 | } 19 | 20 | for (const k in params) { 21 | const field = params[k]; 22 | 23 | v.error(field, 'required', v.getParsedMessage({ 24 | rule: 'required', 25 | attr: field, 26 | value: values[field], 27 | args: params, 28 | })); 29 | } 30 | 31 | v.error('*', 'any', v.getParsedMessage({ 32 | rule: 'any', 33 | attr: '*', 34 | value: values, 35 | args: params, 36 | })); 37 | 38 | return false; 39 | }; 40 | -------------------------------------------------------------------------------- /lib/util/sizeToBytes.js: -------------------------------------------------------------------------------- 1 | /** 2 | * convert size into bytes 3 | * @param {string} inputSize 4 | * @return {Number} 5 | */ 6 | module.exports = function sizeToBytes(inputSize) { 7 | const size = inputSize.toString().toLowerCase(); 8 | 9 | /* istanbul ignore next */ 10 | if (size.includes('gb') || size.includes('g')) { 11 | return parseInt(size.replace('gb', '').replace('g', '')) * 1024 * 1024 * 1024; 12 | } 13 | 14 | /* istanbul ignore next */ 15 | if (size.includes('mb') || size.includes('m')) { 16 | return parseInt(size.replace('mb', '').replace('m', '')) * 1024 * 1024; 17 | } 18 | 19 | /* istanbul ignore next */ 20 | if (size.includes('kb') || size.includes('k')) { 21 | return parseInt(size.replace('kb', '').replace('k', '')) * 1024; 22 | } 23 | 24 | /* istanbul ignore next */ 25 | if (size.includes('b')) { 26 | return parseInt(size.replace('b', '')); 27 | } 28 | 29 | /* istanbul ignore next */ 30 | return parseInt(size) * 1024; 31 | }; 32 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | pull_request: 7 | branches: [master] 8 | 9 | jobs: 10 | test: 11 | runs-on: ubuntu-latest 12 | 13 | strategy: 14 | matrix: 15 | node-version: [12, 14, 16, 18, 20] 16 | 17 | steps: 18 | - uses: actions/checkout@v4 19 | 20 | - name: Use Node.js ${{ matrix.node-version }} 21 | uses: actions/setup-node@v4 22 | with: 23 | node-version: ${{ matrix.node-version }} 24 | 25 | - name: Install dependencies 26 | run: npm install 27 | 28 | - name: Run tests 29 | run: npm test 30 | 31 | - name: Run coverage 32 | if: matrix.node-version == 18 33 | run: npm run coverage 34 | 35 | - name: Upload coverage to Codecov 36 | if: matrix.node-version == 18 37 | uses: codecov/codecov-action@v4 38 | with: 39 | file: ./coverage/lcov.info 40 | fail_ci_if_error: false 41 | -------------------------------------------------------------------------------- /lib/rules/digitsBetween.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable radix */ 2 | const numeric = require('./numeric'); 3 | const integer = require('./integer'); 4 | 5 | module.exports = function digitsBetween({ attr, value, args }) { 6 | // const isNumeric = numeric({ attr,value }); 7 | 8 | if (args.length !== 2) { 9 | throw new Error(`The number of arguments for digitsBetween rule in the field ${attr} are invalid.`); 10 | } 11 | 12 | let [min, max] = args; 13 | 14 | if (!integer({ value: min }) || !integer({ value: max })) { 15 | throw new Error(`Seeds must be integer for ${attr} under digitsBetween rule.`); 16 | } 17 | 18 | if (!numeric({ value })) { 19 | return false; 20 | } 21 | 22 | min = parseInt(min); 23 | max = parseInt(max); 24 | 25 | if (min >= max) { 26 | throw new Error(`Seed min must be less then max in digitsBetween rule for ${attr}.`); 27 | } 28 | 29 | if (value.length < min || value.length > max) { 30 | return false; 31 | } 32 | 33 | return true; 34 | }; 35 | -------------------------------------------------------------------------------- /lib/rules/between.js: -------------------------------------------------------------------------------- 1 | const numeric = require('./numeric'); 2 | 3 | module.exports = function between({ attr, value, args }) { 4 | if (args.length !== 2) { 5 | throw new Error(`The number of arguments for between in the field ${attr} are invalid.`); 6 | } 7 | 8 | let [min, max] = args; 9 | 10 | if (!numeric({ value: min }) || !numeric({ value: max })) { 11 | throw new Error(`Seeds must be numeric for ${attr} under between rule.`); 12 | } 13 | 14 | min = parseFloat(min); 15 | max = parseFloat(max); 16 | 17 | if (min >= max) { 18 | throw new Error(`Seed min must be less then max in between rule for ${attr}.`); 19 | } 20 | 21 | if (Array.isArray(value)) { 22 | if (value.length < min || value.length > max) { 23 | return false; 24 | } 25 | return true; 26 | } 27 | 28 | if (numeric({ value })) { 29 | const val = Number(value); 30 | 31 | if (val < min || val > max) { 32 | return false; 33 | } 34 | 35 | return true; 36 | } 37 | 38 | return false; 39 | }; 40 | -------------------------------------------------------------------------------- /test/postRules/function.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | const { Validator } = require('../../lib/index'); 4 | 5 | describe('function', () => { 6 | it('should use custom function', async () => { 7 | let v = new Validator({ username: 'arnold', password: 'arnold123' }, {}); 8 | 9 | v.addPostRule(async (provider) => { 10 | if (provider.inputs.password.indexOf(provider.inputs.username) >= 0) { 11 | provider.error('password', 'custom', 'Password cannot contain username'); 12 | } 13 | }); 14 | 15 | let matched = await v.check(); 16 | 17 | assert.equal(matched, false); 18 | 19 | v = new Validator({ username: 'arnold', password: '123456' }, {}); 20 | 21 | v.addPostRule(async (provider) => { 22 | if (provider.inputs.password.indexOf(provider.inputs.username) >= 0) { 23 | provider.error('password', 'custom', 'Password cannot contain username'); 24 | } 25 | }); 26 | 27 | matched = await v.check(); 28 | 29 | assert.equal(matched, true); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /test/rules/url.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | const { Validator } = require('../../lib/index'); 4 | 5 | describe('url', () => { 6 | it('should pass', async () => { 7 | const v = new Validator({ url: 'http://www.github.com' }, { url: 'required|url' }); 8 | 9 | const matched = await v.check(); 10 | 11 | assert.equal(matched, true); 12 | }); 13 | 14 | it('should fail', async () => { 15 | const v = new Validator({ url: 'artisangang' }, { url: 'required|url' }); 16 | 17 | const matched = await v.check(); 18 | 19 | assert.equal(matched, false); 20 | }); 21 | 22 | it('message should exist', async () => { 23 | const v = new Validator({ url: '123456' }, { url: 'required|url' }); 24 | 25 | const matched = await v.check(); 26 | 27 | assert.equal(matched, false); 28 | assert.equal( 29 | v.errors.url.message, 30 | v.getExistinParsedMessage({ 31 | rule: 'url', 32 | value: '', 33 | attr: 'url', 34 | args: [], 35 | }), 36 | ); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /test/rules/regex.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | const { Validator } = require('../../lib/index'); 4 | 5 | describe('regex', () => { 6 | it('should pass', async () => { 7 | const v = new Validator( 8 | { number: 'abc' }, { number: 'regex:[abc]' }, 9 | ); 10 | 11 | const matched = await v.check(); 12 | 13 | assert.equal(matched, true); 14 | }); 15 | 16 | it('should fail', async () => { 17 | const v = new Validator( 18 | { attribute: 'xyz' }, { attribute: 'regex:[abc]' }, 19 | ); 20 | 21 | const matched = await v.check(); 22 | 23 | assert.equal(matched, false); 24 | }); 25 | 26 | it('message should exist', async () => { 27 | const v = new Validator( 28 | { attr: '123' }, 29 | { attr: 'regex:[abc]' }, 30 | ); 31 | 32 | const matched = await v.check(); 33 | assert.equal(matched, false); 34 | assert.equal( 35 | v.errors.attr.message, 36 | v.getExistinParsedMessage({ 37 | rule: 'regex', 38 | value: '123', 39 | attr: 'attr', 40 | args: ['[abc]'], 41 | }), 42 | ); 43 | }); 44 | }); 45 | -------------------------------------------------------------------------------- /test/rules/json.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | const { Validator } = require('../../lib/index'); 3 | 4 | describe('json', () => { 5 | it('should pass', async () => { 6 | const v = new Validator( 7 | { attr: '[1, 2, 3]' }, 8 | { attr: 'json' }, 9 | ); 10 | 11 | const matched = await v.check(); 12 | 13 | assert.equal(matched, true); 14 | }); 15 | 16 | it('should fail with string', async () => { 17 | const v = new Validator( 18 | { attr: 'string' }, 19 | { attr: 'json' }, 20 | ); 21 | 22 | const matched = await v.check(); 23 | 24 | assert.equal(matched, false); 25 | }); 26 | 27 | it('message should exist', async () => { 28 | const v = new Validator( 29 | { attr: 'string' }, 30 | { attr: 'json' }, 31 | ); 32 | 33 | const matched = await v.check(); 34 | 35 | assert.equal(matched, false); 36 | 37 | assert.equal( 38 | v.errors.attr.message, 39 | v.getExistinParsedMessage({ 40 | rule: 'json', 41 | value: 'string', 42 | attr: 'attr', 43 | args: [], 44 | }), 45 | ); 46 | }); 47 | }); 48 | -------------------------------------------------------------------------------- /lib/rules/lengthBetween.js: -------------------------------------------------------------------------------- 1 | const integer = require('./integer'); 2 | 3 | module.exports = function lengthBetween({ attr, value, args }) { 4 | if (args.length !== 2) { 5 | throw new Error(`The number of arguments for length between in the field ${attr} are invalid.`); 6 | } 7 | 8 | let [min, max] = args; 9 | 10 | const isIntMin = integer({ value: min }); 11 | const isIntMax = integer({ value: max }); 12 | 13 | if (!isIntMin || !isIntMax) { 14 | throw new Error('Seeds must be integer for lengthBetween rule.'); 15 | } 16 | 17 | min = parseInt(min); 18 | max = parseInt(max); 19 | 20 | if (min >= max) { 21 | throw new Error('Seed min must be less then max in lengthBetween.'); 22 | } 23 | 24 | // if (Array.isArray(attribute)) { 25 | 26 | // if (attribute.length < min || attribute.length > max) { 27 | // return false; 28 | // } 29 | 30 | // return true; 31 | // } 32 | 33 | if (typeof value === 'string' || Array.isArray(value)) { 34 | if (value.length < min || value.length > max) { 35 | return false; 36 | } 37 | 38 | return true; 39 | } 40 | 41 | return false; 42 | }; 43 | -------------------------------------------------------------------------------- /test/rules/mongoId.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | const { Validator } = require('../../lib/index'); 3 | 4 | describe('mongoId', () => { 5 | it('should pass', async () => { 6 | const v = new Validator( 7 | { attr: '5c33010638eb95186574b64a' }, 8 | { attr: 'mongoId' }, 9 | ); 10 | 11 | const matched = await v.check(); 12 | 13 | assert.equal(matched, true); 14 | }); 15 | 16 | it('should fail', async () => { 17 | const v = new Validator( 18 | { attr: '1945690' }, 19 | { attr: 'mongoId' }, 20 | ); 21 | 22 | const matched = await v.check(); 23 | 24 | assert.equal(matched, false); 25 | }); 26 | 27 | it('message should exist', async () => { 28 | const v = new Validator( 29 | { attr: 'string' }, 30 | { attr: 'mongoId' }, 31 | ); 32 | const matched = await v.check(); 33 | 34 | assert.equal(matched, false); 35 | 36 | assert.equal( 37 | v.errors.attr.message, 38 | v.getExistinParsedMessage({ 39 | rule: 'mongoId', 40 | value: 'string', 41 | attr: 'attr', 42 | args: [], 43 | }), 44 | ); 45 | }); 46 | }); 47 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "commonjs": true, 4 | "es6": true, 5 | "node": true, 6 | "mocha": true 7 | }, 8 | "extends": [ 9 | "airbnb-base" 10 | ], 11 | "globals": { 12 | "Atomics": "readonly", 13 | "SharedArrayBuffer": "readonly" 14 | }, 15 | "parserOptions": { 16 | "ecmaVersion": 2018 17 | }, 18 | "ignorePatterns": [ 19 | "cjs/*", 20 | "coverage", 21 | "docs", 22 | "esm", 23 | "node_modules" 24 | ], 25 | "rules": { 26 | "no-plusplus": "off", 27 | "no-continue": "off", 28 | "no-param-reassign": "off", 29 | "no-empty": "off", 30 | "no-throw-literal": "off", 31 | "no-restricted-globals": "off", 32 | "no-underscore-dangle": "off", 33 | "radix": "off", 34 | "no-prototype-builtins": "off", 35 | "class-methods-use-this": "off", 36 | "max-len": [ 37 | "error", 38 | 120 39 | ], 40 | "array-callback-return": "off", 41 | "default-case": "off", 42 | "comma-dangle": "off" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /test/rules/email.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | const { Validator } = require('../../lib/index'); 4 | 5 | describe('email', () => { 6 | it('should pass with valid email', async () => { 7 | const v = new Validator( 8 | { attr: 'user@example.com' }, 9 | { attr: 'email' }, 10 | ); 11 | 12 | const matched = await v.check(); 13 | 14 | assert.equal(matched, true); 15 | }); 16 | 17 | it('should fail', async () => { 18 | const v = new Validator( 19 | { attr: 'form@example' }, 20 | { attr: 'email' }, 21 | ); 22 | 23 | const matched = await v.check(); 24 | 25 | assert.equal(matched, false); 26 | }); 27 | 28 | it('message should exist', async () => { 29 | const v = new Validator( 30 | { attr: 'form@example' }, 31 | { attr: 'email' }, 32 | ); 33 | 34 | const matched = await v.check(); 35 | 36 | assert.equal(matched, false); 37 | assert.equal( 38 | v.errors.attr.message, 39 | v.getExistinParsedMessage({ 40 | rule: 'email', 41 | value: 'form@example', 42 | attr: 'attr', 43 | args: [], 44 | }), 45 | ); 46 | }); 47 | }); 48 | -------------------------------------------------------------------------------- /test/rules/iso1801.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | const { Validator } = require('../../lib/index'); 4 | 5 | describe('iso8601', () => { 6 | it('should pass', async () => { 7 | const v = new Validator( 8 | { attr: '2019-01-07T10:43:59Z' }, 9 | { attr: 'iso8601' }, 10 | ); 11 | 12 | const matched = await v.check(); 13 | 14 | assert.equal(matched, true); 15 | }); 16 | 17 | it('should fail', async () => { 18 | const v = new Validator( 19 | { attr: 'Yes, Node is awesome' }, 20 | { attr: 'iso8601' }, 21 | ); 22 | 23 | const matched = await v.check(); 24 | 25 | assert.equal(matched, false); 26 | }); 27 | 28 | it('message should exist', async () => { 29 | const v = new Validator( 30 | { attr: 'string' }, 31 | { attr: 'iso8601' }, 32 | ); 33 | 34 | const matched = await v.check(); 35 | 36 | assert.equal(matched, false); 37 | 38 | assert.equal( 39 | v.errors.attr.message, 40 | v.getExistinParsedMessage({ 41 | rule: 'iso8601', 42 | value: 'string', 43 | attr: 'attr', 44 | args: [], 45 | }), 46 | ); 47 | }); 48 | }); 49 | -------------------------------------------------------------------------------- /test/rules/latLong.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | const { Validator } = require('../../lib/index'); 4 | 5 | describe('latLong', () => { 6 | it('should pass', async () => { 7 | const v = new Validator( 8 | { attr: '30.483997,76.593948' }, 9 | { attr: 'latLong' }, 10 | ); 11 | 12 | const matched = await v.check(); 13 | 14 | assert.equal(matched, true); 15 | }); 16 | 17 | it('should fail', async () => { 18 | const v = new Validator( 19 | { attr: 'Yes, Node is awesome' }, 20 | { attr: 'latLong' }, 21 | ); 22 | 23 | const matched = await v.check(); 24 | 25 | assert.equal(matched, false); 26 | }); 27 | 28 | it('message should exist', async () => { 29 | const v = new Validator( 30 | { attr: 'string' }, 31 | { attr: 'latLong' }, 32 | ); 33 | 34 | const matched = await v.check(); 35 | 36 | assert.equal(matched, false); 37 | 38 | assert.equal( 39 | v.errors.attr.message, 40 | v.getExistinParsedMessage({ 41 | rule: 'latLong', 42 | value: 'string', 43 | attr: 'attr', 44 | args: [], 45 | }), 46 | ); 47 | }); 48 | }); 49 | -------------------------------------------------------------------------------- /test/rules/ip.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | const { Validator } = require('../../lib/index'); 4 | 5 | describe('ip', () => { 6 | it('should pass', async () => { 7 | const v = new Validator( 8 | { attribute: '192.168.1.14' }, 9 | { attribute: 'ip' }, 10 | ); 11 | 12 | const matched = await v.check(); 13 | 14 | assert.equal(matched, true); 15 | }); 16 | 17 | it('should fail with string', async () => { 18 | const v = new Validator( 19 | { attribute: 'Yes, Node is awesome' }, 20 | { attribute: 'ip' }, 21 | ); 22 | 23 | const matched = await v.check(); 24 | 25 | assert.equal(matched, false); 26 | }); 27 | 28 | it('message should exist', async () => { 29 | const v = new Validator( 30 | { attr: '192.168 25.25' }, 31 | { attr: 'ip' }, 32 | ); 33 | 34 | const matched = await v.check(); 35 | 36 | assert.equal(matched, false); 37 | 38 | assert.equal( 39 | v.errors.attr.message, 40 | v.getExistinParsedMessage({ 41 | rule: 'ip', 42 | value: '192.168 25.25', 43 | attr: 'attr', 44 | args: [], 45 | }), 46 | ); 47 | }); 48 | }); 49 | -------------------------------------------------------------------------------- /lib/rules/requiredIf.js: -------------------------------------------------------------------------------- 1 | const empty = require('../util/empty'); 2 | const { pathIndex } = require('../util/ObjectIndex'); 3 | 4 | module.exports = function requiredIf({ attr, value, args }, v) { 5 | if (!args || args.length < 2) { 6 | throw new Error(`Invalid arguments supplied for field ${attr} in requiredIf rule.`); 7 | } 8 | 9 | if (args.length % 2 !== 0) { 10 | throw new Error(`Invalid arguments supplied for field ${attr} in requiredIf rule.`); 11 | } 12 | 13 | let required = false; 14 | for (let start = 0; start < args.length; start += 2) { 15 | const requiredField = args[start]; 16 | const requiredValue = args[start + 1]; 17 | 18 | if (requiredField === attr) { 19 | return false; 20 | } 21 | 22 | // field is required if all values are presented 23 | if (!empty.reallyEmptyTrimmed(pathIndex(v.inputs, requiredField)) 24 | && pathIndex(v.inputs, requiredField).toString() === requiredValue) { 25 | required = true; 26 | } else { 27 | required = false; 28 | break; 29 | } 30 | } 31 | 32 | if (required && empty.reallyEmptyTrimmed(value)) { 33 | return false; 34 | } 35 | 36 | return true; 37 | }; 38 | -------------------------------------------------------------------------------- /test/rules/dateBefore.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | const { Validator } = require('../../lib/index'); 3 | 4 | describe('#before', () => { 5 | it('should pass', async () => { 6 | const v = new Validator({ dob: '1990-02-28' }, { dob: 'required|dateFormat:YYYY-MM-DD|dateBefore:1991-01-01' }); 7 | 8 | const matched = await v.fails(); 9 | assert.equal(matched, false); 10 | }); 11 | 12 | it('should fail', async () => { 13 | const v = new Validator({ dob: '1993-28-02' }, { dob: 'required|dateFormat:YYYY-MM-DD|dateBefore:1992-12-31' }); 14 | 15 | const matched = await v.passes(); 16 | 17 | assert.equal(matched, false); 18 | }); 19 | 20 | it('message should exist', async () => { 21 | const v = new Validator({ dob: '1993-02-28' }, { dob: 'required|dateFormat:YYYY-MM-DD|before:1992-12-31' }); 22 | 23 | const matched = await v.passes(); 24 | 25 | assert.equal(matched, false); 26 | assert.equal( 27 | v.errors.dob.message, 28 | v.getExistinParsedMessage({ 29 | rule: 'dateBefore', 30 | value: '1992-12-31', 31 | attr: 'dob', 32 | args: ['1992-12-31'], 33 | }), 34 | ); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /test/rules/equals.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | const { Validator } = require('../../lib/index'); 4 | 5 | describe('equals', () => { 6 | it('should pass', async () => { 7 | const v = new Validator( 8 | { attr: 'yes' }, 9 | { attr: 'equals:yes' }, 10 | ); 11 | 12 | const matched = await v.check(); 13 | 14 | assert.equal(matched, true); 15 | }); 16 | 17 | it('should fail', async () => { 18 | const v = new Validator( 19 | { attr: 'Yes, Node is awesome' }, 20 | { attr: 'equals:no' }, 21 | ); 22 | 23 | const matched = await v.check(); 24 | 25 | assert.equal(matched, false); 26 | }); 27 | 28 | it('message should exist', async () => { 29 | const v = new Validator( 30 | { attr: 'Yes, Node is awesome' }, 31 | { attr: 'equals:no' }, 32 | ); 33 | 34 | const matched = await v.check(); 35 | 36 | assert.equal(matched, false); 37 | assert.equal( 38 | v.errors.attr.message, 39 | v.getExistinParsedMessage({ 40 | rule: 'equals', 41 | value: 'Yes, Node is awesome', 42 | attr: 'attr', 43 | args: ['no'], 44 | }), 45 | ); 46 | }); 47 | }); 48 | -------------------------------------------------------------------------------- /test/rules/hex.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | const { Validator } = require('../../lib/index'); 4 | 5 | describe('hex', () => { 6 | it('should pass', async () => { 7 | const v = new Validator( 8 | { attr: '6e6f646520696e7075742076616c696461746f72' }, 9 | { attr: 'hex' }, 10 | ); 11 | 12 | const matched = await v.check(); 13 | 14 | assert.equal(matched, true); 15 | }); 16 | 17 | it('should fail', async () => { 18 | const v = new Validator( 19 | { attr: 'Yes, Node is awesome' }, 20 | { attr: 'hex' }, 21 | ); 22 | 23 | const matched = await v.check(); 24 | 25 | assert.equal(matched, false); 26 | }); 27 | 28 | it('message should exist', async () => { 29 | const v = new Validator( 30 | { attr: 'Yes, Node is awesome' }, 31 | { attr: 'hex' }, 32 | ); 33 | 34 | const matched = await v.check(); 35 | assert.equal(matched, false); 36 | assert.equal( 37 | v.errors.attr.message, 38 | v.getExistinParsedMessage({ 39 | rule: 'hex', 40 | value: 'Yes, Node is awesome', 41 | attr: 'attr', 42 | args: [], 43 | }), 44 | ); 45 | }); 46 | }); 47 | -------------------------------------------------------------------------------- /test/rules/datetime.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | const { Validator } = require('../../lib/index'); 4 | 5 | describe('datetime', () => { 6 | it('should pass', async () => { 7 | const v = new Validator( 8 | { attr: '2019-07-01 10:10:00' }, 9 | { attr: 'datetime' }, 10 | ); 11 | 12 | const matched = await v.check(); 13 | 14 | assert.equal(matched, true); 15 | }); 16 | 17 | it('should fail with invalid format', async () => { 18 | const v = new Validator( 19 | { attr: '01/26/2018' }, 20 | { attr: 'datetime' }, 21 | ); 22 | 23 | const matched = await v.check(); 24 | 25 | assert.equal(matched, false); 26 | }); 27 | 28 | it('message should exist', async () => { 29 | const v = new Validator( 30 | { attr: '12 12 18' }, 31 | { attr: 'datetime' }, 32 | ); 33 | 34 | const matched = await v.check(); 35 | 36 | assert.equal(matched, false); 37 | 38 | assert.equal( 39 | v.errors.attr.message, 40 | v.getExistinParsedMessage({ 41 | rule: 'datetime', 42 | value: '12 12 18', 43 | attr: 'attr', 44 | args: [], 45 | }), 46 | ); 47 | }); 48 | }); 49 | -------------------------------------------------------------------------------- /test/rules/macAddress.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | const { Validator } = require('../../lib/index'); 4 | 5 | describe('macAddress', () => { 6 | it('should pass', async () => { 7 | const v = new Validator( 8 | { attr: '00:14:22:01:23:45' }, 9 | { attr: 'macAddress' }, 10 | ); 11 | 12 | const matched = await v.check(); 13 | 14 | assert.equal(matched, true); 15 | }); 16 | 17 | it('should fail: with string', async () => { 18 | const v = new Validator( 19 | { attr: 'Yes, Node is awesome' }, 20 | { attr: 'macAddress' }, 21 | ); 22 | 23 | const matched = await v.check(); 24 | 25 | assert.equal(matched, false); 26 | }); 27 | 28 | it('message should exist', async () => { 29 | const v = new Validator( 30 | { attr: 'string' }, 31 | { attr: 'macAddress' }, 32 | ); 33 | const matched = await v.check(); 34 | 35 | assert.equal(matched, false); 36 | 37 | assert.equal( 38 | v.errors.attr.message, 39 | v.getExistinParsedMessage({ 40 | rule: 'macAddress', 41 | value: 'string', 42 | attr: 'attr', 43 | args: [], 44 | }), 45 | ); 46 | }); 47 | }); 48 | -------------------------------------------------------------------------------- /test/rules/ascii.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | const { Validator } = require('../../lib/index'); 4 | 5 | describe('ascii', () => { 6 | it('should pass with ascii chars', async () => { 7 | const v = new Validator( 8 | { username: 'sfsf46546*/-=-!@#$%^&*()_+!?><:"{}[];' }, 9 | { username: 'ascii' }, 10 | ); 11 | 12 | const matched = await v.check(); 13 | 14 | assert.equal(matched, true); 15 | }); 16 | 17 | it('should fail with non ascii char', async () => { 18 | const v = new Validator( 19 | { username: 'uname€' }, 20 | { username: 'ascii' }, 21 | ); 22 | 23 | const matched = await v.check(); 24 | 25 | assert.equal(matched, false); 26 | }); 27 | 28 | it('message should exist', async () => { 29 | const v = new Validator( 30 | { username: 'uname€' }, 31 | { username: 'ascii' }, 32 | ); 33 | 34 | const matched = await v.check(); 35 | 36 | assert.equal(matched, false); 37 | 38 | assert.equal( 39 | v.errors.username.message, 40 | v.getExistinParsedMessage({ 41 | rule: 'ascii', 42 | value: {}, 43 | attr: 'username', 44 | }), 45 | ); 46 | }); 47 | }); 48 | -------------------------------------------------------------------------------- /test/rules/contains.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | const { Validator } = require('../../lib/index'); 4 | 5 | describe('contains', () => { 6 | it('should pass with match', async () => { 7 | const v = new Validator( 8 | { attr: 'This package is awesome.' }, 9 | { attr: 'contains:package' }, 10 | ); 11 | 12 | const matched = await v.check(); 13 | 14 | assert.equal(matched, true); 15 | }); 16 | 17 | it('should fail with no-match', async () => { 18 | const v = new Validator( 19 | { attr: 'Yes, Node is awesome' }, 20 | { attr: 'contains:yes' }, 21 | ); 22 | 23 | const matched = await v.check(); 24 | 25 | assert.equal(matched, false); 26 | }); 27 | 28 | it('message should exist', async () => { 29 | const v = new Validator( 30 | { attr: {} }, 31 | { attr: 'contains:yes' }, 32 | ); 33 | 34 | const matched = await v.check(); 35 | 36 | assert.equal(matched, false); 37 | assert.equal( 38 | v.errors.attr.message, 39 | v.getExistinParsedMessage({ 40 | rule: 'contains', 41 | value: {}, 42 | attr: 'attr', 43 | args: ['yes'], 44 | }), 45 | ); 46 | }); 47 | }); 48 | -------------------------------------------------------------------------------- /test/rules/notContains.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | const { Validator } = require('../../lib/index'); 4 | 5 | describe('notContains', () => { 6 | it('should pass', async () => { 7 | const v = new Validator( 8 | { attr: 'This library is awesome.' }, 9 | { attr: 'notContains:package' }, 10 | ); 11 | 12 | const matched = await v.check(); 13 | 14 | assert.equal(matched, true); 15 | }); 16 | 17 | it('should fail', async () => { 18 | const v = new Validator( 19 | { attr: 'Yes, Node is awesome' }, 20 | { attr: 'notContains:Yes' }, 21 | ); 22 | 23 | const matched = await v.check(); 24 | 25 | assert.equal(matched, false); 26 | }); 27 | 28 | it('message should exist', async () => { 29 | const v = new Validator( 30 | { attr: 'yes' }, 31 | { attr: 'notContains:yes' }, 32 | ); 33 | 34 | const matched = await v.check(); 35 | 36 | assert.equal(matched, false); 37 | assert.equal( 38 | v.errors.attr.message, 39 | v.getExistinParsedMessage({ 40 | rule: 'notContains', 41 | value: 'ok', 42 | attr: 'attr', 43 | args: ['yes'], 44 | }), 45 | ); 46 | }); 47 | }); 48 | -------------------------------------------------------------------------------- /test/rules/hash.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | const { Validator } = require('../../lib/index'); 4 | 5 | describe('hash', () => { 6 | it('should pass with md5', async () => { 7 | const v = new Validator( 8 | { attr: '46f8fb7d635cb71beafe8fe580c56164' }, 9 | { attr: 'hash:md5' }, 10 | ); 11 | 12 | const matched = await v.check(); 13 | 14 | assert.equal(matched, true); 15 | }); 16 | 17 | it('should fail with plain text', async () => { 18 | const v = new Validator( 19 | { attr: 'Yes, Node is awesome' }, 20 | { attr: 'hash:md5' }, 21 | ); 22 | 23 | const matched = await v.check(); 24 | 25 | assert.equal(matched, false); 26 | }); 27 | 28 | it('message should exist', async () => { 29 | const v = new Validator( 30 | { attr: '123456456789' }, 31 | { attr: 'hash:md5' }, 32 | ); 33 | 34 | const matched = await v.check(); 35 | 36 | assert.equal(matched, false); 37 | assert.equal( 38 | v.errors.attr.message, 39 | v.getExistinParsedMessage({ 40 | rule: 'hash', 41 | value: '123456456789', 42 | attr: 'attr', 43 | args: ['md5'], 44 | }), 45 | ); 46 | }); 47 | }); 48 | -------------------------------------------------------------------------------- /test/rules/nullable.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | const { Validator } = require('../../lib/index'); 4 | 5 | describe('nullable', () => { 6 | it('should fail', async () => { 7 | const v = new Validator( 8 | { attr: 'email' }, 9 | { attr: 'nullable|email' }, 10 | ); 11 | 12 | const matched = await v.check(); 13 | 14 | assert.equal(matched, false); 15 | }); 16 | 17 | it('should fail, attr absent', async () => { 18 | const v = new Validator( 19 | {}, 20 | { attr: 'nullable|email' }, 21 | ); 22 | 23 | const matched = await v.check(); 24 | 25 | assert.equal(matched, false); 26 | }); 27 | 28 | it('should pass, attr is null', async () => { 29 | const v = new Validator( 30 | { attr: null }, 31 | { attr: 'nullable|email' }, 32 | ); 33 | 34 | const matched = await v.check(); 35 | 36 | assert.equal(matched, true); 37 | }); 38 | 39 | it('should pass, attr is null, ignore required', async () => { 40 | const v = new Validator( 41 | { attr: null }, 42 | { attr: 'nullable|alpha|required' }, 43 | ); 44 | 45 | const matched = await v.check(); 46 | 47 | assert.equal(matched, true); 48 | }); 49 | }); 50 | -------------------------------------------------------------------------------- /test/rules/notIn.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | const { Validator } = require('../../lib/index'); 4 | 5 | describe('notIn', () => { 6 | it('should pass', async () => { 7 | const v = new Validator( 8 | { attribute: 'public' }, 9 | { attribute: 'notIn:private,draft' }, 10 | ); 11 | 12 | const matched = await v.check(); 13 | 14 | assert.equal(matched, true); 15 | }); 16 | 17 | it('should fail: misnotIng attribute', async () => { 18 | const v = new Validator( 19 | { attribute: 'draft' }, 20 | { attribute: 'notIn:public,draft' }, 21 | ); 22 | 23 | const matched = await v.check(); 24 | 25 | assert.equal(matched, false); 26 | }); 27 | 28 | it('message should exist', async () => { 29 | const v = new Validator( 30 | { attr: 'draft' }, 31 | { attr: 'notIn:draft,public,private' }, 32 | ); 33 | const matched = await v.check(); 34 | assert.equal(matched, false); 35 | assert.equal( 36 | v.errors.attr.message, 37 | v.getExistinParsedMessage({ 38 | rule: 'notIn', 39 | value: 'draft', 40 | attr: 'attr', 41 | args: ['draft', 'public', 'private'], 42 | }), 43 | ); 44 | }); 45 | }); 46 | -------------------------------------------------------------------------------- /test/rules/base64.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | const { Validator } = require('../../lib/index'); 4 | 5 | describe('base64', () => { 6 | it('should pass with valida base64 string', async () => { 7 | const v = new Validator( 8 | { username: 'dGhpcyBpcyB0ZXN0aW5nLi4u' }, 9 | { username: 'base64' }, 10 | ); 11 | 12 | const matched = await v.check(); 13 | 14 | assert.equal(matched, true); 15 | }); 16 | 17 | it('should fail with invalid base64 string', async () => { 18 | const v = new Validator( 19 | { username: 'gYhKkdInjUnjUUmkH' }, 20 | { username: 'base64' }, 21 | ); 22 | 23 | const matched = await v.check(); 24 | 25 | assert.equal(matched, false); 26 | }); 27 | 28 | it('message should exist', async () => { 29 | const v = new Validator( 30 | { username: 'gYhKkdInjUnjUUmkH' }, 31 | { username: 'base64' }, 32 | ); 33 | 34 | const matched = await v.check(); 35 | 36 | assert.equal(matched, false); 37 | 38 | assert.equal( 39 | v.errors.username.message, 40 | v.getExistinParsedMessage({ 41 | rule: 'base64', 42 | value: 'gYhKkdInjUnjUUmkH', 43 | attr: 'username', 44 | }), 45 | ); 46 | }); 47 | }); 48 | -------------------------------------------------------------------------------- /lib/rules/acceptedIf.js: -------------------------------------------------------------------------------- 1 | const empty = require('../util/empty'); 2 | const { pathIndex } = require('../util/ObjectIndex'); 3 | 4 | module.exports = function acceptedIf({ attr, value, args }, validator) { 5 | if (!args || args.length < 2) { 6 | throw new Error(`Invalid arguments supplied for field ${attr} in acceptedIf rule.`); 7 | } 8 | 9 | if (args.length % 2 !== 0) { 10 | throw new Error(`Invalid arguments supplied for field ${attr} in acceptedIf rule.`); 11 | } 12 | 13 | const acceptedValues = [true, 'true', 1, '1', 'yes', 'on']; 14 | 15 | let canbetrue = false; 16 | for (let start = 0; start < args.length; start += 2) { 17 | const requiredField = args[start]; 18 | const requiredValue = args[start + 1]; 19 | 20 | if (requiredField === attr) { 21 | return false; 22 | } 23 | 24 | // field can be true if all values are presented 25 | if (!empty.reallyEmptyTrimmed(pathIndex(validator.inputs, requiredField)) 26 | && pathIndex(validator.inputs, requiredField).toString() === requiredValue) { 27 | canbetrue = true; 28 | } else { 29 | canbetrue = false; 30 | break; 31 | } 32 | } 33 | if (canbetrue && !(acceptedValues.indexOf(value) >= 0)) { 34 | return false; 35 | } 36 | 37 | return true; 38 | }; 39 | -------------------------------------------------------------------------------- /lib/rules/acceptedNotIf.js: -------------------------------------------------------------------------------- 1 | const empty = require('../util/empty'); 2 | const { pathIndex } = require('../util/ObjectIndex'); 3 | 4 | module.exports = function acceptedNotIf({ attr, value, args }, validator) { 5 | if (!args || args.length < 2) { 6 | throw new Error(`Invalid arguments supplied for attribute ${attr} in acceptedNotIf rule.`); 7 | } 8 | 9 | if (args.length % 2 !== 0) { 10 | throw new Error(`Invalid arguments supplied for attribute ${attr} in acceptedNotIf rule.`); 11 | } 12 | 13 | const acceptedValues = [true, 'true', 1, '1', 'yes', 'on']; 14 | 15 | let canbetrue = false; 16 | for (let start = 0; start < args.length; start += 2) { 17 | const requiredField = args[start]; 18 | const requiredValue = args[start + 1]; 19 | 20 | if (requiredField === attr) { 21 | return false; 22 | } 23 | 24 | // field can be true if all values are presented 25 | if (!empty.reallyEmptyTrimmed(pathIndex(validator.inputs, requiredField)) 26 | && pathIndex(validator.inputs, requiredField).toString() === requiredValue) { 27 | canbetrue = true; 28 | } else { 29 | canbetrue = false; 30 | break; 31 | } 32 | } 33 | if (canbetrue && acceptedValues.indexOf(value) >= 0) { 34 | return false; 35 | } 36 | 37 | return true; 38 | }; 39 | -------------------------------------------------------------------------------- /test/async.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | const niv = require('../lib/index'); 4 | 5 | const { Validator } = niv; 6 | 7 | niv.extend('asyncIn', async ({ value, args }) => { 8 | const results = await new Promise((resolve) => { 9 | setTimeout(() => { 10 | if (args.indexOf(value) >= 0) { 11 | resolve(true); 12 | return; 13 | } 14 | 15 | resolve(false); 16 | }, 500); 17 | }); 18 | return results; 19 | }); 20 | 21 | describe('Async Rules', () => { 22 | it('should pass using async', async () => { 23 | const v = new Validator( 24 | { status: 'active' }, { status: 'asyncIn:active,inactive' }, 25 | ); 26 | 27 | const matched = await v.check(); 28 | 29 | assert.equal(matched, true); 30 | }); 31 | 32 | it('should pass using async and sync', async () => { 33 | const v = new Validator( 34 | { status: 'active' }, { status: 'string|asyncIn:active,inactive|ascii' }, 35 | ); 36 | 37 | const matched = await v.check(); 38 | 39 | assert.equal(matched, true); 40 | }); 41 | 42 | it('should fails using async', async () => { 43 | const v = new Validator( 44 | { status: 'active' }, { status: 'asyncIn:activated,deactivated' }, 45 | ); 46 | 47 | const matched = await v.check(); 48 | 49 | assert.equal(matched, false); 50 | }); 51 | }); 52 | -------------------------------------------------------------------------------- /test/rules/phoneNumber.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | const { Validator } = require('../../lib/index'); 4 | 5 | describe('phoneNumber', () => { 6 | it('should pass', async () => { 7 | const v = new Validator({ id: '+918699987073' }, { id: 'phoneNumber' }); 8 | 9 | const matched = await v.check(); 10 | 11 | assert.equal(matched, true); 12 | }); 13 | 14 | it('should pass with hypens', async () => { 15 | const v = new Validator({ id: '+1-541-754-3010' }, { id: 'phoneNumber' }); 16 | const matched = await v.check(); 17 | 18 | assert.equal(matched, true); 19 | }); 20 | 21 | it('should fail with string', async () => { 22 | const v = new Validator({ attribute: 'artisangang' }, { attribute: 'phoneNumber' }); 23 | 24 | const matched = await v.check(); 25 | 26 | assert.equal(matched, false); 27 | }); 28 | 29 | it('message should exist', async () => { 30 | const v = new Validator( 31 | { attr: 'draft' }, 32 | { attr: 'phoneNumber' }, 33 | ); 34 | 35 | const matched = await v.check(); 36 | assert.equal(matched, false); 37 | assert.equal( 38 | v.errors.attr.message, 39 | v.getExistinParsedMessage({ 40 | rule: 'phoneNumber', 41 | value: 'draft', 42 | attr: 'attr', 43 | args: [], 44 | }), 45 | ); 46 | }); 47 | }); 48 | -------------------------------------------------------------------------------- /test/rules/different.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | const { Validator } = require('../../lib/index'); 4 | 5 | describe('different', () => { 6 | it('should pass', async () => { 7 | const v = new Validator( 8 | { password: '000000', new_password: '123456' }, 9 | { password: 'required', new_password: 'required|different:password' }, 10 | ); 11 | 12 | const matched = await v.check(); 13 | 14 | assert.equal(matched, true); 15 | }); 16 | 17 | it('should fail', async () => { 18 | const v = new Validator( 19 | { password: '000000', new_password: '000000' }, 20 | { password: 'required', new_password: 'required|different:password' }, 21 | ); 22 | 23 | const matched = await v.check(); 24 | 25 | assert.equal(matched, false); 26 | }); 27 | 28 | it('message should exist', async () => { 29 | const v = new Validator( 30 | { password: '000000', new_password: '000000' }, 31 | { password: 'required', new_password: 'required|different:password' }, 32 | ); 33 | 34 | const matched = await v.check(); 35 | 36 | assert.equal(matched, false); 37 | assert.equal( 38 | v.errors.new_password.message, 39 | v.getExistinParsedMessage({ 40 | rule: 'different', 41 | value: '', 42 | attr: 'new_password', 43 | args: ['password'], 44 | }), 45 | ); 46 | }); 47 | }); 48 | -------------------------------------------------------------------------------- /test/rules/same.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | const { Validator } = require('../../lib/index'); 4 | 5 | describe('same', () => { 6 | it('should pass', async () => { 7 | const v = new Validator( 8 | { password: '000000', confirm_password: '000000' }, 9 | { password: 'required', confirm_password: 'required|same:password' }, 10 | ); 11 | 12 | const matched = await v.check(); 13 | 14 | assert.equal(matched, true); 15 | }); 16 | 17 | it('should fail', async () => { 18 | const v = new Validator( 19 | { password: '000000', confirm_password: '123456' }, 20 | { password: 'required', confirm_password: 'required|same:password' }, 21 | ); 22 | 23 | const matched = await v.check(); 24 | 25 | assert.equal(matched, false); 26 | }); 27 | 28 | it('message should exist', async () => { 29 | const v = new Validator( 30 | { password: '000000', confirm_password: '123456' }, 31 | { password: 'required', confirm_password: 'required|same:password' }, 32 | ); 33 | 34 | const matched = await v.check(); 35 | 36 | assert.equal(matched, false); 37 | assert.equal( 38 | v.errors.confirm_password.message, 39 | v.getExistinParsedMessage({ 40 | rule: 'same', 41 | value: '', 42 | attr: 'confirm_password', 43 | args: ['password'], 44 | }), 45 | ); 46 | }); 47 | }); 48 | -------------------------------------------------------------------------------- /test/rules/accepted.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | const { Validator } = require('../../lib/index'); 4 | 5 | describe('accepted', () => { 6 | it('should pass with yes', async () => { 7 | const v = new Validator( 8 | { attr: 'yes' }, 9 | { attr: 'accepted' }, 10 | ); 11 | 12 | const matched = await v.check(); 13 | 14 | assert.equal(matched, true); 15 | }); 16 | 17 | it('should pass with ok using custom options', async () => { 18 | const v = new Validator( 19 | { attr: 'ok' }, 20 | { attr: 'accepted:ok' }, 21 | ); 22 | 23 | const matched = await v.check(); 24 | 25 | assert.equal(matched, true); 26 | }); 27 | 28 | it('should fail using no', async () => { 29 | const v = new Validator( 30 | { attr: 'no' }, 31 | { attr: 'accepted' }, 32 | ); 33 | 34 | const matched = await v.check(); 35 | 36 | assert.equal(matched, false); 37 | }); 38 | 39 | it('message should exist', async () => { 40 | const v = new Validator( 41 | {}, 42 | { attr: 'accepted' }, 43 | ); 44 | 45 | const matched = await v.check(); 46 | 47 | assert.equal(matched, false); 48 | 49 | assert.equal( 50 | v.errors.attr.message, 51 | v.getExistinParsedMessage({ 52 | rule: 'accepted', 53 | value: undefined, 54 | attr: 'attr', 55 | }), 56 | ); 57 | }); 58 | }); 59 | -------------------------------------------------------------------------------- /test/rules/array.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | const { Validator } = require('../../lib/index'); 4 | 5 | describe('array', () => { 6 | it('should pass with empty array', async () => { 7 | const v = new Validator( 8 | { features: [] }, 9 | { features: 'array' }, 10 | ); 11 | 12 | const matched = await v.check(); 13 | 14 | assert.equal(matched, true); 15 | }); 16 | 17 | it('should pass with filled array', async () => { 18 | const v = new Validator( 19 | { features: [1, 2, 3] }, 20 | { features: 'array' }, 21 | ); 22 | 23 | const matched = await v.check(); 24 | 25 | assert.equal(matched, true); 26 | }); 27 | 28 | it('should fail with string', async () => { 29 | const v = new Validator( 30 | { features: 'no' }, 31 | { features: 'array' }, 32 | ); 33 | 34 | const matched = await v.check(); 35 | 36 | assert.equal(matched, false); 37 | }); 38 | 39 | it('message should exist', async () => { 40 | const v = new Validator( 41 | { features: {} }, 42 | { features: 'array' }, 43 | ); 44 | 45 | const matched = await v.check(); 46 | 47 | assert.equal(matched, false); 48 | assert.equal( 49 | v.errors.features.message, 50 | v.getExistinParsedMessage({ 51 | rule: 'array', 52 | value: {}, 53 | attr: 'features', 54 | }), 55 | ); 56 | }); 57 | }); 58 | -------------------------------------------------------------------------------- /test/rules/object.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | const { Validator } = require('../../lib/index'); 3 | 4 | describe('object', () => { 5 | it('should pass with empty {}', async () => { 6 | const v = new Validator( 7 | { features: {} }, 8 | { features: 'object' }, 9 | ); 10 | 11 | const matched = await v.check(); 12 | 13 | assert.equal(matched, true); 14 | }); 15 | 16 | it('should pass', async () => { 17 | const v = new Validator( 18 | { features: { status: 'draft' } }, 19 | { features: 'object' }, 20 | ); 21 | 22 | const matched = await v.check(); 23 | 24 | assert.equal(matched, true); 25 | }); 26 | 27 | it('should fail with string', async () => { 28 | const v = new Validator( 29 | { features: 'no' }, 30 | { features: 'object' }, 31 | ); 32 | 33 | const matched = await v.check(); 34 | 35 | assert.equal(matched, false); 36 | }); 37 | 38 | it('message should exist', async () => { 39 | const v = new Validator( 40 | { attr: 'draft' }, 41 | { attr: 'object' }, 42 | ); 43 | 44 | const matched = await v.check(); 45 | assert.equal(matched, false); 46 | assert.equal( 47 | v.errors.attr.message, 48 | v.getExistinParsedMessage({ 49 | rule: 'object', 50 | value: 'draft', 51 | attr: 'attr', 52 | args: [], 53 | }), 54 | ); 55 | }); 56 | }); 57 | -------------------------------------------------------------------------------- /test/rules/in.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | const { Validator } = require('../../lib/index'); 4 | 5 | describe('in', () => { 6 | it('should pass', async () => { 7 | const v = new Validator( 8 | { attr: 'public' }, 9 | { attr: 'in:private,public,draft' }, 10 | ); 11 | 12 | const matched = await v.check(); 13 | 14 | assert.equal(matched, true); 15 | }); 16 | 17 | it('should pass with single', async () => { 18 | const v = new Validator( 19 | { attr: 'public' }, 20 | { attr: 'in:public' }, 21 | ); 22 | 23 | const matched = await v.check(); 24 | 25 | assert.equal(matched, true); 26 | }); 27 | 28 | it('should fail with missing one', async () => { 29 | const v = new Validator( 30 | { attr: 'draft' }, 31 | { attr: 'in:public,private' }, 32 | ); 33 | 34 | const matched = await v.check(); 35 | 36 | assert.equal(matched, false); 37 | }); 38 | 39 | it('message should exist', async () => { 40 | const v = new Validator( 41 | { attr: 'draft' }, 42 | { attr: 'in:public,private' }, 43 | ); 44 | const matched = await v.check(); 45 | assert.equal(matched, false); 46 | assert.equal( 47 | v.errors.attr.message, 48 | v.getExistinParsedMessage({ 49 | rule: 'in', 50 | value: 'draft', 51 | attr: 'attr', 52 | args: ['public', 'private'], 53 | }), 54 | ); 55 | }); 56 | }); 57 | -------------------------------------------------------------------------------- /lib/rules/size.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | const fs = require('fs'); 3 | const sizeToBytes = require('../util/sizeToBytes'); 4 | 5 | module.exports = function validateSize({ value, args }) { 6 | const file = value; 7 | 8 | let min; let size; 9 | 10 | const max = sizeToBytes(args[0]); 11 | if (args.length >= 2) { 12 | min = sizeToBytes(args[1]); 13 | } 14 | // } else { 15 | // max = sizeToBytes(args); 16 | // } 17 | 18 | if (file.size) { 19 | size = file.size; 20 | } else if (typeof file === 'string') { 21 | try { 22 | size = fs.statSync(file).size; 23 | } catch (e) { 24 | /* istanbul ignore next */ 25 | console.error(e); 26 | } 27 | } else if (file.path && typeof file.path === 'string') { 28 | try { 29 | size = fs.statSync(file.path).size; 30 | } catch (e) { 31 | /* istanbul ignore next */ 32 | console.error(e); 33 | } 34 | } else if (file instanceof Buffer) { 35 | size = file.byteLength; 36 | } else if (file.buffer && file.buffer instanceof Buffer) { 37 | size = file.buffer.byteLength; 38 | } else { 39 | throw new Error('Size rule only accepts Buffer,file path or size property in file object.'); 40 | } 41 | 42 | if (!max) { 43 | return false; 44 | } 45 | 46 | if (max && size >= max) { 47 | return false; 48 | } 49 | 50 | if (min && size <= min) { 51 | return false; 52 | } 53 | 54 | return true; 55 | }; 56 | -------------------------------------------------------------------------------- /test/rules/date.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | const { Validator } = require('../../lib/index'); 4 | 5 | describe('date', () => { 6 | it('should pass supported format', async () => { 7 | const v = new Validator( 8 | { attribute: '2018-12-26' }, 9 | { attribute: 'date' }, 10 | ); 11 | 12 | const matched = await v.check(); 13 | 14 | assert.equal(matched, true); 15 | }); 16 | 17 | it('should fail not supported format', async () => { 18 | const v = new Validator( 19 | { attribute: '01/26/2018' }, 20 | { attribute: 'date' }, 21 | ); 22 | 23 | const matched = await v.check(); 24 | 25 | assert.equal(matched, false); 26 | }); 27 | 28 | it('should fail with invalid value', async () => { 29 | const v = new Validator( 30 | { attribute: '12 12 18' }, 31 | { attribute: 'date' }, 32 | ); 33 | 34 | const matched = await v.check(); 35 | 36 | assert.equal(matched, false); 37 | }); 38 | 39 | it('message should exist', async () => { 40 | const v = new Validator( 41 | { attr: '12122018' }, 42 | { attr: 'date' }, 43 | ); 44 | 45 | const matched = await v.check(); 46 | 47 | assert.equal(matched, false); 48 | assert.equal( 49 | v.errors.attr.message, 50 | v.getExistinParsedMessage({ 51 | rule: 'date', 52 | value: '12122018', 53 | attr: 'attr', 54 | }), 55 | ); 56 | }); 57 | }); 58 | -------------------------------------------------------------------------------- /test/rules/dateAfter.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | const { Validator } = require('../../lib/index'); 3 | 4 | describe('#dateAfter', () => { 5 | it('should pass with validadate format', async () => { 6 | const v = new Validator( 7 | { dob: '1990-02-28' }, 8 | { dob: 'required|dateFormat:YYYY-MM-DD|dateAfter:1990-01-01' }, 9 | ); 10 | 11 | const matched = await v.check(); 12 | assert.equal(matched, true); 13 | }); 14 | 15 | it('should fail with invalida date format', async () => { 16 | let v = new Validator({ dob: '1993-28-02' }, { dob: 'required|dateFormat:YYYY-MM-DD|dateAfter:2000-15-31' }); 17 | 18 | let matched = await v.check(); 19 | 20 | assert.equal(matched, false); 21 | 22 | v = new Validator({ dob: '1993-28-02' }, { dob: 'required|dateFormat:YYYY-MM-DD|after:2000-12-31' }); 23 | 24 | matched = await v.check(); 25 | 26 | assert.equal(matched, false); 27 | }); 28 | 29 | it('message should exist', async () => { 30 | const v = new Validator({ dob: '1993-10-15' }, { dob: 'required|dateFormat:YYYY-MM-DD|after:2000-12-31' }); 31 | 32 | const matched = await v.check(); 33 | 34 | assert.equal(matched, false); 35 | assert.equal( 36 | v.errors.dob.message, 37 | v.getExistinParsedMessage({ 38 | rule: 'after', 39 | value: '1993-10-15', 40 | attr: 'dob', 41 | args: ['2000-12-31'], 42 | }), 43 | ); 44 | }); 45 | }); 46 | -------------------------------------------------------------------------------- /lib/rules/dimensions.js: -------------------------------------------------------------------------------- 1 | const sizeOf = require('image-size'); 2 | const namedArgs = require('../util/namedArgs'); 3 | 4 | module.exports = function dimensions({ value, args }) { 5 | const rules = namedArgs(args); 6 | 7 | let inputFile; 8 | 9 | if (typeof value === 'string' || value instanceof Buffer) { 10 | inputFile = value; 11 | } else if (value.path && typeof value.path === 'string') { 12 | inputFile = value.path; 13 | } else if (value.buffer && value.buffer instanceof Buffer) { 14 | inputFile = value.buffer; 15 | } else { 16 | throw new Error('Dimensions rule only accepts Buffer,file path or size property in file object.'); 17 | } 18 | 19 | const imgDimensions = sizeOf(inputFile); 20 | 21 | if (rules.minWidth && Number(rules.minWidth) > imgDimensions.width) { 22 | return false; 23 | } 24 | if (rules.maxWidth && Number(rules.maxWidth) < imgDimensions.width) { 25 | return false; 26 | } 27 | 28 | // throw Number(rules.minHeight) < imgDimensions.height; 29 | if (rules.minHeight && Number(rules.minHeight) > imgDimensions.height) { 30 | return false; 31 | } 32 | 33 | if (rules.maxHeight && Number(rules.maxHeight) < imgDimensions.height) { 34 | return false; 35 | } 36 | 37 | if (rules.width && Number(rules.width) !== imgDimensions.width) { 38 | return false; 39 | } 40 | if (rules.height && Number(rules.height) !== imgDimensions.height) { 41 | return false; 42 | } 43 | 44 | return true; 45 | }; 46 | -------------------------------------------------------------------------------- /lib/util/obj.js: -------------------------------------------------------------------------------- 1 | let wildcardIterations = 1000; 2 | 3 | 4 | function esc(key, options) { 5 | if (typeof options.escape === 'function') { 6 | return options.escape(key, options); 7 | } 8 | return key.split(options.separator).join(`\\${options.separator}`); 9 | } 10 | 11 | exports.setStrNotationRepetition = (repetition) => wildcardIterations = repetition; 12 | 13 | exports.strNotations = function strNotations(target, customize = {}) { 14 | const options = { 15 | separator: '.', 16 | repetition: wildcardIterations, 17 | values: true, 18 | ...customize, 19 | }; 20 | const sep = options.separator; 21 | const values = {}; 22 | const keys = []; 23 | 24 | let currentRep = 0; 25 | 26 | function parse(obj, prev) { 27 | currentRep++; 28 | 29 | if (currentRep >= options.repetition) { 30 | // eslint-disable-next-line no-console 31 | throw new Error(`Max(${options.repetition}) repetation was reached.`); 32 | } 33 | 34 | const objKeys = Object.keys(obj); 35 | 36 | objKeys.forEach((k) => { 37 | const val = obj[k]; 38 | const key = (prev ? prev + sep : '') + esc(k, options); 39 | 40 | if (Array.isArray(val) || (val !== null && typeof val === 'object')) { 41 | parse(val, key); 42 | keys.push(key); 43 | values[key] = val; 44 | } else { 45 | keys.push(key); 46 | values[key] = val; 47 | } 48 | }); 49 | } 50 | 51 | parse(target); 52 | return options.values ? values : keys; 53 | }; 54 | -------------------------------------------------------------------------------- /test/rules/dateFormat.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | const { Validator } = require('../../lib/index'); 4 | 5 | describe('dateFormat', () => { 6 | it('should pass with valid date', async () => { 7 | const v = new Validator( 8 | { attr: '2018-12-26' }, 9 | { attr: 'dateFormat:YYYY-MM-DD' }, 10 | ); 11 | 12 | const matched = await v.passes(); 13 | 14 | assert.equal(matched, true); 15 | }); 16 | 17 | it('should pass', async () => { 18 | const v = new Validator( 19 | { attr: '2018/01/26' }, 20 | { attr: 'dateFormat:YYYY/MM/DD' }, 21 | ); 22 | 23 | const matched = await v.passes(); 24 | 25 | assert.equal(matched, true); 26 | }); 27 | 28 | it('should fail with invalid format', async () => { 29 | const v = new Validator( 30 | { attr: '12 12 18' }, 31 | { attr: 'dateFormat:YYYY-MM-DD' }, 32 | ); 33 | 34 | const matched = await v.fails(); 35 | 36 | assert.equal(matched, true); 37 | }); 38 | 39 | it('message should exist', async () => { 40 | const v = new Validator( 41 | { attr: '28/08/2019' }, 42 | { attr: 'dateFormat:YYYY-MM-DD' }, 43 | ); 44 | 45 | const matched = await v.fails(); 46 | 47 | assert.equal(matched, true); 48 | assert.equal( 49 | v.errors.attr.message, 50 | v.getExistinParsedMessage({ 51 | rule: 'dateFormat', 52 | value: '28/08/2019', 53 | attr: 'attr', 54 | args: ['YYYY-MM-DD'], 55 | }), 56 | ); 57 | }); 58 | }); 59 | -------------------------------------------------------------------------------- /test/rules/dateBeforeToday.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | const moment = require('moment'); 4 | const { Validator } = require('../../lib/index'); 5 | 6 | describe('#dateBeforeToday', () => { 7 | it('should pass', async () => { 8 | const v = new Validator( 9 | { 10 | dob: moment().subtract(1, 'days').format('YYYY-MM-DD'), 11 | }, 12 | { 13 | dob: 'required|dateFormat:YYYY-MM-DD|dateBeforeToday:1', 14 | }, 15 | ); 16 | 17 | const matched = await v.passes(); 18 | assert.equal(matched, true); 19 | }); 20 | 21 | it('should fail', async () => { 22 | const v = new Validator( 23 | { 24 | dob: moment().format('YYYY-MM-DD'), 25 | }, 26 | { 27 | dob: 'required|dateFormat:YYYY-MM-DD|dateBeforeToday:2,days', 28 | }, 29 | ); 30 | 31 | const matched = await v.check(); 32 | 33 | assert.equal(matched, false); 34 | }); 35 | 36 | it('message should exist', async () => { 37 | const v = new Validator( 38 | { 39 | dob: moment().format('YYYY-MM-DD'), 40 | }, 41 | { 42 | dob: 'required|dateFormat:YYYY-MM-DD|dateBeforeToday:2,days', 43 | }, 44 | ); 45 | 46 | const matched = await v.check(); 47 | 48 | assert.equal(matched, false); 49 | assert.equal( 50 | v.errors.dob.message, 51 | v.getExistinParsedMessage({ 52 | rule: 'dateBeforeToday', 53 | value: moment().format('YYYY-MM-DD'), 54 | attr: 'dob', 55 | args: ['2', 'days'], 56 | }), 57 | ); 58 | }); 59 | }); 60 | -------------------------------------------------------------------------------- /test/rules/minLength.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | const { Validator } = require('../../lib/index'); 4 | 5 | describe('minLength', () => { 6 | it('should pass', async () => { 7 | const v = new Validator( 8 | { attr: 'uname' }, 9 | { attr: 'minLength:5' }, 10 | ); 11 | 12 | const matched = await v.check(); 13 | 14 | assert.equal(matched, true); 15 | }); 16 | 17 | it('should fail', async () => { 18 | const v = new Validator( 19 | { attr: 'uname' }, 20 | { attr: 'minLength:6' }, 21 | ); 22 | 23 | const matched = await v.check(); 24 | 25 | assert.equal(matched, false); 26 | }); 27 | 28 | it('should throw invalid seed exception', async () => { 29 | try { 30 | const v = new Validator({ attribute: 'Harcharan Singh' }, { attribute: 'required|minLength:test' }); 31 | 32 | await v.check(); 33 | 34 | throw new Error('Invalid seed exception.'); 35 | } catch (e) { 36 | assert.equal(e, 'Error: Seed in minLength rule for attribute must be a number.'); 37 | } 38 | }); 39 | 40 | it('message should exist', async () => { 41 | const v = new Validator( 42 | { attr: 'string' }, 43 | { attr: 'minLength:10' }, 44 | ); 45 | const matched = await v.check(); 46 | 47 | assert.equal(matched, false); 48 | 49 | assert.equal( 50 | v.errors.attr.message, 51 | v.getExistinParsedMessage({ 52 | rule: 'minLength', 53 | value: 'string', 54 | attr: 'attr', 55 | args: [10], 56 | }), 57 | ); 58 | }); 59 | }); 60 | -------------------------------------------------------------------------------- /test/rules/maxLength.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | const { Validator } = require('../../lib/index'); 4 | 5 | describe('maxLength', () => { 6 | it('should pass', async () => { 7 | const v = new Validator( 8 | { attr: 'uname' }, 9 | { attr: 'maxLength:10' }, 10 | ); 11 | 12 | const matched = await v.check(); 13 | 14 | assert.equal(matched, true); 15 | }); 16 | 17 | it('should fail with max length', async () => { 18 | const v = new Validator( 19 | { attr: 'uname' }, 20 | { attr: 'maxLength:4' }, 21 | ); 22 | 23 | const matched = await v.check(); 24 | 25 | assert.equal(matched, false); 26 | }); 27 | 28 | it('should throw invalid seed exception', async () => { 29 | try { 30 | const v = new Validator({ attribute: 'Harcharan Singh' }, { attribute: 'required|maxLength:test' }); 31 | 32 | await v.check(); 33 | 34 | throw new Error('Invalid seed exception.'); 35 | } catch (e) { 36 | assert.equal(e, 'Error: Seed in maxLength rule for attribute must be a number.'); 37 | } 38 | }); 39 | 40 | it('message should exist', async () => { 41 | const v = new Validator( 42 | { attr: 'string' }, 43 | { attr: 'maxLength:4' }, 44 | ); 45 | const matched = await v.check(); 46 | 47 | assert.equal(matched, false); 48 | 49 | assert.equal( 50 | v.errors.attr.message, 51 | v.getExistinParsedMessage({ 52 | rule: 'maxLength', 53 | value: 'string', 54 | attr: 'attr', 55 | args: [4], 56 | }), 57 | ); 58 | }); 59 | }); 60 | -------------------------------------------------------------------------------- /lib/util/messageParser.js: -------------------------------------------------------------------------------- 1 | const { camelToSentance, snakeToSentance } = require('./str'); 2 | 3 | /** 4 | * parse message 5 | * @param {*} param0 6 | * @return {string} 7 | */ 8 | module.exports = function messageParser({ 9 | args, attr, value, message = '', 10 | }) { 11 | let defaultMessage = message; 12 | 13 | // replace attribute name 14 | if (defaultMessage.indexOf(':attribute') >= 0) { 15 | // convert camel to sentance and replce _ with space 16 | let attributeName = attr || ''; 17 | 18 | if (attributeName.indexOf('.') < 0) { 19 | attributeName = camelToSentance(snakeToSentance(attr)); 20 | } 21 | 22 | defaultMessage = defaultMessage.replace(':attribute', attributeName); 23 | } 24 | 25 | // replace args 26 | if (defaultMessage.indexOf(':args') >= 0) { 27 | defaultMessage = defaultMessage.replace(':args', args.toString()); 28 | } 29 | 30 | // find and replace each arg 31 | for (let i = 0; i < 10; i++) { 32 | if (defaultMessage.indexOf(`:arg${i}`) >= 0) { 33 | defaultMessage = defaultMessage.replace(`:arg${i}`, args[i]); 34 | } else { 35 | break; 36 | } 37 | } 38 | 39 | if (defaultMessage.indexOf(':value') >= 0) { 40 | /* istanbul ignore next */ 41 | if (typeof value === 'object') { 42 | defaultMessage = defaultMessage.replace(':value', JSON.stringify(value)); 43 | } else if (typeof value === 'undefined') { 44 | defaultMessage = defaultMessage.replace(':value', 'undefined'); 45 | } else { 46 | defaultMessage = defaultMessage.replace(':value', value.toString()); 47 | } 48 | } 49 | 50 | return defaultMessage; 51 | }; 52 | -------------------------------------------------------------------------------- /test/rules/integer.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | const { Validator } = require('../../lib/index'); 4 | 5 | describe('integer', () => { 6 | it('should pass with integer as string', async () => { 7 | const v = new Validator( 8 | { attr: '12' }, 9 | { attr: 'integer' }, 10 | ); 11 | 12 | const matched = await v.check(); 13 | 14 | assert.equal(matched, true); 15 | }); 16 | 17 | it('should pass with int', async () => { 18 | const v = new Validator( 19 | { attr: 12 }, 20 | { attr: 'integer' }, 21 | ); 22 | 23 | const matched = await v.check(); 24 | 25 | assert.equal(matched, true); 26 | }); 27 | 28 | it('should fail with decimal', async () => { 29 | const v = new Validator( 30 | { attr: 12.5 }, 31 | { attr: 'integer' }, 32 | ); 33 | 34 | const matched = await v.check(); 35 | 36 | assert.equal(matched, false); 37 | }); 38 | 39 | it('should fail with string', async () => { 40 | const v = new Validator( 41 | { attr: 'draft' }, 42 | { attr: 'integer' }, 43 | ); 44 | 45 | const matched = await v.check(); 46 | 47 | assert.equal(matched, false); 48 | }); 49 | 50 | it('message should exist', async () => { 51 | const v = new Validator( 52 | { attr: 'draft' }, 53 | { attr: 'integer' }, 54 | ); 55 | 56 | const matched = await v.check(); 57 | assert.equal(matched, false); 58 | assert.equal( 59 | v.errors.attr.message, 60 | v.getExistinParsedMessage({ 61 | rule: 'integer', 62 | value: 'draft', 63 | attr: 'attr', 64 | args: [], 65 | }), 66 | ); 67 | }); 68 | }); 69 | -------------------------------------------------------------------------------- /test/rules/numeric.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | const { Validator } = require('../../lib/index'); 4 | 5 | describe('numeric', () => { 6 | it('should pass with numbers in string', async () => { 7 | const v = new Validator( 8 | { attr: '12' }, 9 | { attr: 'numeric' }, 10 | ); 11 | 12 | const matched = await v.check(); 13 | 14 | assert.equal(matched, true); 15 | }); 16 | 17 | it('should pass with integer', async () => { 18 | const v = new Validator( 19 | { attr: 12 }, 20 | { attr: 'numeric' }, 21 | ); 22 | 23 | const matched = await v.check(); 24 | 25 | assert.equal(matched, true); 26 | }); 27 | 28 | it('should pass with decimal', async () => { 29 | const v = new Validator( 30 | { attr: 12.5 }, 31 | { attr: 'numeric' }, 32 | ); 33 | 34 | const matched = await v.check(); 35 | 36 | assert.equal(matched, true); 37 | }); 38 | 39 | it('should fail with non numeric', async () => { 40 | const v = new Validator( 41 | { attr: 'draft' }, 42 | { attr: 'numeric' }, 43 | ); 44 | 45 | const matched = await v.check(); 46 | 47 | assert.equal(matched, false); 48 | }); 49 | 50 | it('message should exist', async () => { 51 | const v = new Validator( 52 | { attr: 'draft' }, 53 | { attr: 'numeric' }, 54 | ); 55 | 56 | const matched = await v.check(); 57 | assert.equal(matched, false); 58 | assert.equal( 59 | v.errors.attr.message, 60 | v.getExistinParsedMessage({ 61 | rule: 'numeric', 62 | value: 'draft', 63 | attr: 'attr', 64 | args: [], 65 | }), 66 | ); 67 | }); 68 | }); 69 | -------------------------------------------------------------------------------- /test/rules/domain.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | const { Validator } = require('../../lib/index'); 4 | 5 | describe('domain', () => { 6 | it('should pass with example.com', async () => { 7 | const v = new Validator( 8 | { attr: 'example.com' }, 9 | { attr: 'domain' }, 10 | ); 11 | 12 | const matched = await v.check(); 13 | 14 | assert.equal(matched, true); 15 | }); 16 | 17 | it('should pass with www', async () => { 18 | const v = new Validator( 19 | { attr: 'www.example.com' }, 20 | { attr: 'domain' }, 21 | ); 22 | 23 | const matched = await v.check(); 24 | 25 | assert.equal(matched, true); 26 | }); 27 | 28 | it('should fail with http', async () => { 29 | const v = new Validator( 30 | { attr: 'http://www.example.com' }, 31 | { attr: 'domain' }, 32 | ); 33 | 34 | const matched = await v.check(); 35 | 36 | assert.equal(matched, false); 37 | }); 38 | 39 | it('should fail with string', async () => { 40 | const v = new Validator( 41 | { attr: 'localhost' }, 42 | { attr: 'domain' }, 43 | ); 44 | 45 | const matched = await v.check(); 46 | 47 | assert.equal(matched, false); 48 | }); 49 | 50 | it('message should exist', async () => { 51 | const v = new Validator( 52 | { attr: '123' }, 53 | { attr: 'domain' }, 54 | ); 55 | 56 | const matched = await v.check(); 57 | 58 | assert.equal(matched, false); 59 | assert.equal( 60 | v.errors.attr.message, 61 | v.getExistinParsedMessage({ 62 | rule: 'domain', 63 | value: '123', 64 | attr: 'attr', 65 | args: [], 66 | }), 67 | ); 68 | }); 69 | }); 70 | -------------------------------------------------------------------------------- /test/rules/lt.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | const { Validator } = require('../../lib/index'); 4 | 5 | describe('lt', () => { 6 | it('should pass', async () => { 7 | const v = new Validator( 8 | { min: '20', max: '25' }, 9 | { 10 | min: 'required|integer|lt:max', 11 | max: 'required|integer', 12 | }, 13 | ); 14 | 15 | const matched = await v.check(); 16 | 17 | assert.equal(matched, true); 18 | }); 19 | 20 | it('should fail', async () => { 21 | const v = new Validator( 22 | { min: '30', max: '25' }, 23 | { 24 | min: 'required|integer|lt:max', 25 | max: 'required|integer', 26 | }, 27 | ); 28 | 29 | const matched = await v.check(); 30 | 31 | assert.equal(matched, false); 32 | }); 33 | 34 | it('should fail due to nan in another field', async () => { 35 | const v = new Validator( 36 | { min: '30', max: 'abc' }, 37 | { 38 | min: 'required|integer|lt:max', 39 | max: 'required', 40 | }, 41 | ); 42 | 43 | const matched = await v.check(); 44 | 45 | assert.equal(matched, false); 46 | }); 47 | 48 | it('message should exist', async () => { 49 | const v = new Validator( 50 | { min: 'abc', max: 'abc' }, 51 | { 52 | min: 'required|lt:max', 53 | max: 'required', 54 | }, 55 | ); 56 | const matched = await v.check(); 57 | 58 | assert.equal(matched, false); 59 | 60 | assert.equal( 61 | v.errors.min.message, 62 | v.getExistinParsedMessage({ 63 | rule: 'lt', 64 | value: '30', 65 | attr: 'min', 66 | args: ['max'], 67 | }), 68 | ); 69 | }); 70 | }); 71 | -------------------------------------------------------------------------------- /test/rules/gt.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | const { Validator } = require('../../lib/index'); 4 | 5 | describe('gt', () => { 6 | it('should pass', async () => { 7 | const v = new Validator( 8 | { min: '20', max: '25' }, 9 | { 10 | min: 'required|integer', 11 | max: 'required|integer|gt:min', 12 | }, 13 | ); 14 | 15 | const matched = await v.check(); 16 | 17 | assert.equal(matched, true); 18 | }); 19 | 20 | it('should fail with min', async () => { 21 | const v = new Validator( 22 | { min: '30', max: '25' }, 23 | { 24 | min: 'required|integer', 25 | max: 'required|integer|gt:min', 26 | }, 27 | ); 28 | 29 | const matched = await v.check(); 30 | 31 | assert.equal(matched, false); 32 | }); 33 | 34 | it('should fail due to nan in another field', async () => { 35 | const v = new Validator( 36 | { min: 'abc', max: '25' }, 37 | { 38 | min: 'required', 39 | max: 'required|integer|gt:min', 40 | }, 41 | ); 42 | 43 | const matched = await v.check(); 44 | 45 | assert.equal(matched, false); 46 | }); 47 | 48 | it('message should exist', async () => { 49 | const v = new Validator( 50 | { min: 'abc', max: 'abc' }, 51 | { 52 | min: 'required', 53 | max: 'required|gt:min', 54 | }, 55 | ); 56 | 57 | const matched = await v.check(); 58 | 59 | assert.equal(matched, false); 60 | assert.equal( 61 | v.errors.max.message, 62 | v.getExistinParsedMessage({ 63 | rule: 'gt', 64 | value: '25', 65 | attr: 'max', 66 | args: ['min'], 67 | }), 68 | ); 69 | }); 70 | }); 71 | -------------------------------------------------------------------------------- /test/rules/hexColor.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | const { Validator } = require('../../lib/index'); 4 | 5 | describe('hexColor', () => { 6 | it('should pass', async () => { 7 | const v = new Validator( 8 | { attr: '#FFFFFF' }, 9 | { attr: 'hexColor' }, 10 | ); 11 | 12 | const matched = await v.check(); 13 | 14 | assert.equal(matched, true); 15 | }); 16 | 17 | it('should pass short code', async () => { 18 | const v = new Validator( 19 | { attr: '#000' }, 20 | { attr: 'hexColor' }, 21 | ); 22 | 23 | const matched = await v.check(); 24 | 25 | assert.equal(matched, true); 26 | }); 27 | 28 | it('should pass without hash', async () => { 29 | const v = new Validator( 30 | { attr: 'f00' }, 31 | { attr: 'hexColor' }, 32 | ); 33 | 34 | const matched = await v.check(); 35 | 36 | assert.equal(matched, true); 37 | }); 38 | 39 | it('should fail with plain text', async () => { 40 | const v = new Validator( 41 | { attr: 'Yes, Node is awesome' }, 42 | { attr: 'hexColor' }, 43 | ); 44 | 45 | const matched = await v.check(); 46 | 47 | assert.equal(matched, false); 48 | }); 49 | 50 | it('message should exist', async () => { 51 | const v = new Validator( 52 | { attr: 'Yes, Node is awesome' }, 53 | { attr: 'hexColor' }, 54 | ); 55 | 56 | const matched = await v.check(); 57 | assert.equal(matched, false); 58 | assert.equal( 59 | v.errors.attr.message, 60 | v.getExistinParsedMessage({ 61 | rule: 'hexColor', 62 | value: 'Yes, Node is awesome', 63 | attr: 'attr', 64 | args: [], 65 | }), 66 | ); 67 | }); 68 | }); 69 | -------------------------------------------------------------------------------- /test/rules/dateiso.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | const { Validator } = require('../../lib/index'); 4 | 5 | describe('dateiso', () => { 6 | it('should pass', async () => { 7 | const v = new Validator( 8 | { attr: '2019-07-01T10:10:00' }, 9 | { attr: 'dateiso' }, 10 | ); 11 | 12 | const matched = await v.passes(); 13 | 14 | assert.equal(matched, true); 15 | }); 16 | 17 | it('should pass', async () => { 18 | const v = new Validator( 19 | { attr: '2019-07-01T10:10:00.00Z' }, 20 | { attr: 'dateiso' }, 21 | ); 22 | 23 | const matched = await v.passes(); 24 | 25 | assert.equal(matched, true); 26 | }); 27 | 28 | it('should fail with invalid format', async () => { 29 | const v = new Validator( 30 | { attr: '01/26/2018' }, 31 | { attr: 'dateiso' }, 32 | ); 33 | 34 | const matched = await v.fails(); 35 | 36 | assert.equal(matched, true); 37 | }); 38 | 39 | it('should fail with invalid value', async () => { 40 | const v = new Validator( 41 | { attr: '12 12 18' }, 42 | { attr: 'dateiso' }, 43 | ); 44 | 45 | const matched = await v.check(); 46 | 47 | assert.equal(matched, false); 48 | }); 49 | 50 | it('message should exist', async () => { 51 | const v = new Validator( 52 | { attr: '12 12 18' }, 53 | { attr: 'dateiso' }, 54 | ); 55 | 56 | const matched = await v.check(); 57 | 58 | assert.equal(matched, false); 59 | assert.equal( 60 | v.errors.attr.message, 61 | v.getExistinParsedMessage({ 62 | rule: 'dateiso', 63 | value: '12 12 18', 64 | attr: 'attr', 65 | args: [], 66 | }), 67 | ); 68 | }); 69 | }); 70 | -------------------------------------------------------------------------------- /test/rules/arrayUnique.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | const { Validator } = require('../../lib/index'); 4 | 5 | describe('arrayUnique', () => { 6 | it('should fail with string', async () => { 7 | const v = new Validator( 8 | { features: 'test' }, 9 | { features: 'arrayUnique' }, 10 | ); 11 | 12 | const matched = await v.check(); 13 | 14 | assert.equal(matched, false); 15 | }); 16 | 17 | it('should pass with empty array', async () => { 18 | const v = new Validator( 19 | { features: [] }, 20 | { features: 'arrayUnique' }, 21 | ); 22 | 23 | const matched = await v.check(); 24 | 25 | assert.equal(matched, true); 26 | }); 27 | 28 | it('should pass with filled array', async () => { 29 | const v = new Validator( 30 | { features: [1, 2, 3] }, 31 | { features: 'arrayUnique' }, 32 | ); 33 | 34 | const matched = await v.check(); 35 | 36 | assert.equal(matched, true); 37 | }); 38 | 39 | it('should fail for duplicates values in array', async () => { 40 | const v = new Validator( 41 | { features: [1, 2, 3, 1] }, 42 | { features: 'arrayUnique' }, 43 | ); 44 | 45 | const matched = await v.check(); 46 | 47 | assert.equal(matched, false); 48 | }); 49 | 50 | it('message should exist', async () => { 51 | const v = new Validator( 52 | { features: {} }, 53 | { features: 'arrayUnique' }, 54 | ); 55 | 56 | const matched = await v.check(); 57 | 58 | assert.equal(matched, false); 59 | assert.equal( 60 | v.errors.features.message, 61 | v.getExistinParsedMessage({ 62 | rule: 'arrayUnique', 63 | value: {}, 64 | attr: 'features', 65 | }), 66 | ); 67 | }); 68 | }); 69 | -------------------------------------------------------------------------------- /test/rules/max.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | const { Validator } = require('../../lib/index'); 4 | 5 | describe('max', () => { 6 | it('should pass with string', async () => { 7 | const v = new Validator( 8 | { attr: '20' }, 9 | { attr: 'max:20' }, 10 | ); 11 | 12 | const matched = await v.check(); 13 | 14 | assert.equal(matched, true); 15 | }); 16 | 17 | it('should pass with integer', async () => { 18 | const v = new Validator( 19 | { attr: 18 }, 20 | { attr: 'max:20' }, 21 | ); 22 | 23 | const matched = await v.check(); 24 | 25 | assert.equal(matched, true); 26 | }); 27 | 28 | it('should fail', async () => { 29 | const v = new Validator( 30 | { attr: '19' }, 31 | { attr: 'max:18' }, 32 | ); 33 | 34 | const matched = await v.check(); 35 | 36 | assert.equal(matched, false); 37 | }); 38 | 39 | it('should throw invalid seed exception', async () => { 40 | try { 41 | const v = new Validator({ attribute: 'Harcharan Singh' }, { attribute: 'required|max:test' }); 42 | 43 | await v.check(); 44 | 45 | throw new Error('Invalid seed exception.'); 46 | } catch (e) { 47 | assert.equal(e, 'Error: Seed in max rule for attribute must be a number.'); 48 | } 49 | }); 50 | 51 | it('message should exist', async () => { 52 | const v = new Validator( 53 | { attr: 'string' }, 54 | { attr: 'max:20' }, 55 | ); 56 | const matched = await v.check(); 57 | 58 | assert.equal(matched, false); 59 | 60 | assert.equal( 61 | v.errors.attr.message, 62 | v.getExistinParsedMessage({ 63 | rule: 'max', 64 | value: 'string', 65 | attr: 'attr', 66 | args: [20], 67 | }), 68 | ); 69 | }); 70 | }); 71 | -------------------------------------------------------------------------------- /test/rules/dateAfterToday.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | const moment = require('moment'); 4 | const { Validator } = require('../../lib/index'); 5 | 6 | describe('#dateAfterToday', () => { 7 | it('should pass with valida date', async () => { 8 | const v = new Validator( 9 | { 10 | dob: moment().add(2, 'days').format('YYYY-MM-DD'), 11 | }, { 12 | dob: 'required|dateFormat:YYYY-MM-DD|dateAfterToday:1,days', 13 | }, 14 | ); 15 | 16 | const matched = await v.check(); 17 | assert.equal(matched, true); 18 | }); 19 | 20 | it('should pass with optional seed', async () => { 21 | const v = new Validator( 22 | { 23 | dob: moment().add(2, 'days').format('YYYY-MM-DD'), 24 | }, { 25 | dob: 'required|dateFormat:YYYY-MM-DD|dateAfterToday:1', 26 | }, 27 | ); 28 | 29 | const matched = await v.check(); 30 | assert.equal(matched, true); 31 | }); 32 | 33 | it('should fail', async () => { 34 | const v = new Validator( 35 | { 36 | dob: '2019-02-28', 37 | }, { 38 | dob: 'required|dateFormat:YYYY-MM-DD|dateAfterToday:2,days', 39 | }, 40 | ); 41 | 42 | const matched = await v.check(); 43 | 44 | assert.equal(matched, false); 45 | }); 46 | 47 | it('message should exist', async () => { 48 | const v = new Validator( 49 | { 50 | dob: '2019-02-28', 51 | }, { 52 | dob: 'required|dateFormat:YYYY-MM-DD|dateAfterToday:2,days', 53 | }, 54 | ); 55 | 56 | const matched = await v.check(); 57 | 58 | assert.equal(matched, false); 59 | assert.equal( 60 | v.errors.dob.message, 61 | v.getExistinParsedMessage({ 62 | rule: 'dateAfterToday', 63 | value: '2019-02-28', 64 | attr: 'dob', 65 | args: ['2', 'days'], 66 | }), 67 | ); 68 | }); 69 | }); 70 | -------------------------------------------------------------------------------- /test/rules/requiredWithout.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | const { Validator } = require('../../lib/index'); 4 | 5 | describe('requiredWithout', () => { 6 | it('should return false for missing seed length', async () => { 7 | try { 8 | const v = new Validator({ age: 16 }, { remember: 'requiredWithout' }); 9 | await v.check(); 10 | throw new Error('Exception was expected.'); 11 | } catch (e) { 12 | assert.equal(e, 'Error: Invalid arguments supplied for field remember in requiredWithout rule.'); 13 | } 14 | }); 15 | 16 | it('should pass', async () => { 17 | const v = new Validator( 18 | { name: 'Harcharan Singh', sex: '', age: '26' }, 19 | { sex: 'requiredWithout:age' }, 20 | ); 21 | 22 | const matched = await v.check(); 23 | assert.equal(matched, true); 24 | }); 25 | 26 | it('should pass', async () => { 27 | const v = new Validator( 28 | { name: 'Harcharan Singh', sex: 'male', age: '26' }, 29 | { sex: 'requiredWithout:age' }, 30 | ); 31 | 32 | const matched = await v.check(); 33 | assert.equal(matched, true); 34 | }); 35 | 36 | it('should fails', async () => { 37 | const v = new Validator( 38 | { name: 'Harcharan Singh' }, 39 | { sex: 'requiredWithout:age' }, 40 | ); 41 | 42 | const matched = await v.check(); 43 | assert.equal(matched, false); 44 | }); 45 | 46 | it('message should exist', async () => { 47 | const v = new Validator( 48 | { name: 'Harcharan Singh' }, 49 | { sex: 'requiredWithout:age' }, 50 | ); 51 | 52 | const matched = await v.check(); 53 | 54 | assert.equal(matched, false); 55 | 56 | assert.equal( 57 | v.errors.sex.message, 58 | v.getExistinParsedMessage({ 59 | rule: 'requiredWithout', 60 | value: '', 61 | attr: 'sex', 62 | args: ['age'], 63 | }), 64 | ); 65 | }); 66 | }); 67 | -------------------------------------------------------------------------------- /test/rules/decimal.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | const { Validator } = require('../../lib/index'); 4 | 5 | describe('decimal', () => { 6 | it('should pass with numbers in string', async () => { 7 | const v = new Validator( 8 | { attr: '12.50' }, 9 | { attr: 'decimal' }, 10 | ); 11 | 12 | const matched = await v.check(); 13 | 14 | assert.equal(matched, true); 15 | }); 16 | 17 | it('should pass with number value', async () => { 18 | const v = new Validator( 19 | { attr: 12.55 }, 20 | { attr: 'decimal' }, 21 | ); 22 | 23 | const matched = await v.check(); 24 | 25 | assert.equal(matched, true); 26 | }); 27 | 28 | it('should pass with integer value', async () => { 29 | const v = new Validator( 30 | { attr: 12 }, 31 | { attr: 'decimal' }, 32 | ); 33 | 34 | const matched = await v.check(); 35 | 36 | assert.equal(matched, true); 37 | }); 38 | 39 | it('should pass with 0 as integer', async () => { 40 | const v = new Validator( 41 | { attr: 0 }, 42 | { attr: 'required|decimal' }, 43 | ); 44 | 45 | const matched = await v.check(); 46 | assert.equal(matched, true); 47 | }); 48 | 49 | it('should pass with 0 as string', async () => { 50 | const v = new Validator( 51 | { attr: '0' }, 52 | { attr: 'required|decimal' }, 53 | ); 54 | 55 | const matched = await v.check(); 56 | 57 | assert.equal(matched, true); 58 | }); 59 | 60 | it('message should exist', async () => { 61 | const v = new Validator( 62 | { attr: 'a12' }, 63 | { attr: 'decimal' }, 64 | ); 65 | 66 | const matched = await v.check(); 67 | 68 | assert.equal(matched, false); 69 | assert.equal( 70 | v.errors.attr.message, 71 | v.getExistinParsedMessage({ 72 | rule: 'decimal', 73 | value: 'a12', 74 | attr: 'attr', 75 | args: [], 76 | }), 77 | ); 78 | }); 79 | }); 80 | -------------------------------------------------------------------------------- /index.d.ts: -------------------------------------------------------------------------------- 1 | export function setLang(lang: string): void; 2 | 3 | export function extend(name: string, callback: any): void; 4 | 5 | export function extendMessages(newMessages: object, lang?: string): void; 6 | 7 | export function addCustomMessages(customMessages: object, lang?: string): void; 8 | 9 | export function niceNames(attributes: object, lang?: string): void; 10 | 11 | export function koa(): Function; 12 | 13 | export function bailable(sure: boolean): void; 14 | 15 | export function assert(rules: object): any; 16 | 17 | export function setStrNotationRepetition(repetition: number): void; 18 | 19 | export declare class Validator { 20 | 21 | inputs: any; 22 | errors: any; 23 | postValidation: any; 24 | filters: any; 25 | lang: string; 26 | customMessages: any; 27 | attributeNames: any; 28 | wasFailed: any; 29 | doBail: any; 30 | validationRules: any; 31 | 32 | constructor(inputs: object, rules: object, customMessages?: object); 33 | 34 | bail(sure?: boolean): void; 35 | 36 | niceNames(attributeNames: object): void; 37 | 38 | parseRules(validationRules: object): void; 39 | 40 | getErrors(): any; 41 | 42 | validate(): Promise; 43 | 44 | check(): Promise; 45 | 46 | fails(): Promise; 47 | 48 | passes(): Promise; 49 | 50 | parseValue(attribute: string): any; 51 | 52 | applyOnDeep(attributes: any): Promise; 53 | 54 | parseNestedAttr(attribute: any): void; 55 | 56 | apply(attr: string): boolean | undefined; 57 | 58 | postApply(rule: any): Promise; 59 | 60 | addPostRule(rule: any): void; 61 | 62 | addPostRules(rulesObject: any): void; 63 | 64 | getParsedMessage(options: any): string; 65 | 66 | getExistinParsedMessage(options: any): string; 67 | 68 | error(key: string, rule: string, message: string): void; 69 | 70 | addError(key: string, rule: string, message: string): void; 71 | 72 | appendError(key: string, rule: string, message: string): void; 73 | } 74 | -------------------------------------------------------------------------------- /test/rules/creditCard.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | const { Validator } = require('../../lib/index'); 4 | 5 | describe('creditCard', () => { 6 | it('should pass with visa card', async () => { 7 | const v = new Validator( 8 | { cc: '4111111111111111' }, 9 | { cc: 'creditCard' }, 10 | ); 11 | 12 | const matched = await v.check(); 13 | 14 | assert.equal(matched, true); 15 | }); 16 | 17 | it('should pass with spaced visa card', async () => { 18 | const v = new Validator( 19 | { cc: '4111 1111 1111 1111' }, 20 | { cc: 'creditCard' }, 21 | ); 22 | 23 | const matched = await v.check(); 24 | 25 | assert.equal(matched, true); 26 | }); 27 | 28 | it('should pass with master card', async () => { 29 | const v = new Validator( 30 | { cc: '5500 0000 0000 0004' }, 31 | { cc: 'creditCard' }, 32 | ); 33 | 34 | const matched = await v.check(); 35 | 36 | assert.equal(matched, true); 37 | }); 38 | 39 | it('should pass with amex', async () => { 40 | const v = new Validator( 41 | { cc: '3400 0000 0000 009' }, 42 | { cc: 'creditCard' }, 43 | ); 44 | 45 | const matched = await v.check(); 46 | 47 | assert.equal(matched, true); 48 | }); 49 | 50 | it('should fail with invalid card', async () => { 51 | const v = new Validator( 52 | { cc: '412365' }, 53 | { cc: 'creditCard' }, 54 | ); 55 | 56 | const matched = await v.check(); 57 | 58 | assert.equal(matched, false); 59 | }); 60 | 61 | it('message should exist', async () => { 62 | const v = new Validator( 63 | { cc: '412365' }, 64 | { cc: 'creditCard' }, 65 | ); 66 | 67 | const matched = await v.check(); 68 | 69 | assert.equal(matched, false); 70 | assert.equal( 71 | v.errors.cc.message, 72 | v.getExistinParsedMessage({ 73 | rule: 'creditCard', 74 | value: '412365', 75 | attr: 'cc', 76 | }), 77 | ); 78 | }); 79 | }); 80 | -------------------------------------------------------------------------------- /test/rules/alpha.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | const { Validator } = require('../../lib/index'); 4 | 5 | describe('alpha', () => { 6 | it('should pass with example', async () => { 7 | const v = new Validator( 8 | { username: 'example' }, 9 | { username: 'alpha' }, 10 | ); 11 | 12 | const matched = await v.check(); 13 | 14 | assert.strictEqual(matched, true); 15 | }); 16 | 17 | it('should pass with example', async () => { 18 | const v = new Validator( 19 | { username: 'está' }, 20 | { username: 'alpha:pt-BR' }, 21 | ); 22 | 23 | const matched = await v.check(); 24 | 25 | assert.strictEqual(matched, true); 26 | }); 27 | 28 | it('should fail with alpha-numeric value', async () => { 29 | const v = new Validator( 30 | { username: 'now123' }, 31 | { username: 'alpha' }, 32 | ); 33 | 34 | const matched = await v.check(); 35 | 36 | assert.equal(matched, false); 37 | }); 38 | 39 | it('should fail with special char', async () => { 40 | const v = new Validator( 41 | { username: 'u@name' }, 42 | { username: 'alpha' }, 43 | ); 44 | 45 | const matched = await v.check(); 46 | 47 | assert.equal(matched, false); 48 | }); 49 | 50 | it('should fail with numbers', async () => { 51 | const v = new Validator( 52 | { username: '123' }, 53 | { username: 'alpha' }, 54 | ); 55 | 56 | const matched = await v.check(); 57 | 58 | assert.equal(matched, false); 59 | }); 60 | 61 | it('message should exist', async () => { 62 | const v = new Validator( 63 | { username: '123' }, 64 | { username: 'alpha' }, 65 | ); 66 | 67 | const matched = await v.check(); 68 | 69 | assert.equal(matched, false); 70 | 71 | assert.equal( 72 | v.errors.username.message, 73 | v.getExistinParsedMessage({ 74 | rule: 'alpha', 75 | value: '123', 76 | attr: 'username', 77 | }), 78 | ); 79 | }); 80 | }); 81 | -------------------------------------------------------------------------------- /test/rules/boolean.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | const { Validator } = require('../../lib/index'); 4 | 5 | describe('boolean', () => { 6 | it('should pass with boolean(true/false)', async () => { 7 | const v = new Validator( 8 | { a: true, b: false }, 9 | { a: 'boolean', b: 'boolean' }, 10 | ); 11 | 12 | const matched = await v.check(); 13 | 14 | assert.equal(matched, true); 15 | }); 16 | 17 | it('should pass with String(true,false)', async () => { 18 | const v = new Validator( 19 | { a: 'false', b: 'false' }, 20 | { a: 'boolean', b: 'boolean' }, 21 | ); 22 | 23 | const matched = await v.check(); 24 | 25 | assert.equal(matched, true); 26 | }); 27 | 28 | it('should pass with Int(0,1)', async () => { 29 | const v = new Validator( 30 | { a: 0, b: 1 }, 31 | { a: 'boolean', b: 'boolean' }, 32 | ); 33 | 34 | const matched = await v.check(); 35 | 36 | assert.equal(matched, true); 37 | }); 38 | 39 | it('should pass with custom seed', async () => { 40 | const v = new Validator( 41 | { a: 'yes', b: 'no' }, 42 | { a: 'boolean:yes,no', b: 'boolean:yes,no' }, 43 | ); 44 | 45 | const matched = await v.check(); 46 | 47 | assert.equal(matched, true); 48 | }); 49 | 50 | it('should fail invalid value', async () => { 51 | const v = new Validator( 52 | { a: 'not accepted' }, 53 | { a: 'boolean' }, 54 | ); 55 | 56 | const matched = await v.check(); 57 | 58 | assert.equal(matched, false); 59 | }); 60 | 61 | it('message should exist', async () => { 62 | const v = new Validator( 63 | { featured: {} }, 64 | { featured: 'boolean' }, 65 | ); 66 | 67 | const matched = await v.check(); 68 | 69 | assert.equal(matched, false); 70 | assert.equal( 71 | v.errors.featured.message, 72 | v.getExistinParsedMessage({ 73 | rule: 'boolean', 74 | value: {}, 75 | attr: 'featured', 76 | }), 77 | ); 78 | }); 79 | }); 80 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-input-validator", 3 | "version": "4.6.0", 4 | "description": "validation library for nodejs, inspired by laravel.", 5 | "main": "lib/index.js", 6 | "scripts": { 7 | "lint": "./node_modules/.bin/eslint .", 8 | "lintf": "./node_modules/.bin/eslint . --fix", 9 | "test": "mocha --recursive", 10 | "coverage": "nyc --reporter=lcov npm run test", 11 | "report-coverage": "nyc report --reporter=text-lcov > coverage.lcov && codecov" 12 | }, 13 | "engines": { 14 | "node": ">=12" 15 | }, 16 | "keywords": [ 17 | "validation", 18 | "validator", 19 | "data validation", 20 | "input validation", 21 | "input validator", 22 | "form validation", 23 | "form validator", 24 | "node validator", 25 | "laravel", 26 | "validator for js", 27 | "koa", 28 | "express", 29 | "file validation", 30 | "image", 31 | "mimes" 32 | ], 33 | "repository": "bitnbytesio/node-input-validator", 34 | "author": { 35 | "name": "Harcharan Singh", 36 | "email": "bitnbytesio@gmail.com" 37 | }, 38 | "contributors": [ 39 | { 40 | "name": "Jakub Nietrzeba", 41 | "url": "https://github.com/gluth" 42 | }, 43 | { 44 | "name": "Sergi Baños", 45 | "url": "https://github.com/SergiBanos" 46 | }, 47 | { 48 | "name": "Nitin Mehra", 49 | "url": "https://github.com/cybersultan" 50 | } 51 | ], 52 | "license": "ISC", 53 | "devDependencies": { 54 | "@types/node": "^12.19.14", 55 | "@types/validator": "^10.11.3", 56 | "eslint": "^8.57.1", 57 | "eslint-config-airbnb-base": "^15.0.0", 58 | "eslint-plugin-import": "^2.29.1", 59 | "mocha": "^9.2.2", 60 | "nyc": "^15.1.0", 61 | "should": "^13.2.3" 62 | }, 63 | "dependencies": { 64 | "file-type": "^16.5.4", 65 | "image-size": "^0.8.3", 66 | "lodash.has": "^4.5.2", 67 | "mime-types": "^2.1.28", 68 | "moment": "^2.29.4", 69 | "read-chunk": "^3.2.0", 70 | "validator": "^13.7.0" 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /test/rules/acceptedNotIf.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | const { Validator } = require('../../lib/index'); 4 | 5 | describe('acceptedNotIf', () => { 6 | it('should pass with no', async () => { 7 | const v = new Validator( 8 | { attr: 'no', age: 16 }, 9 | { attr: 'acceptedNotIf:age,16' }, 10 | ); 11 | 12 | const matched = await v.check(); 13 | 14 | assert.equal(matched, true); 15 | }); 16 | 17 | it('should fail with yes', async () => { 18 | const v = new Validator( 19 | { attr: 'yes', age: 16 }, 20 | { attr: 'acceptedNotIf:age,16' }, 21 | ); 22 | 23 | const matched = await v.check(); 24 | 25 | assert.equal(matched, false); 26 | }); 27 | 28 | it('should throw exception', async () => { 29 | try { 30 | const v = new Validator({ attribute: 'Harcharan Singh' }, { attribute: 'required|acceptedNotIf' }); 31 | 32 | await v.check(); 33 | 34 | throw new Error('Invalid seed exception.'); 35 | } catch (e) { 36 | assert.equal(e, 'Error: Invalid arguments supplied for attribute attribute in acceptedNotIf rule.'); 37 | } 38 | 39 | try { 40 | const v = new Validator({ attribute: 'Harcharan Singh' }, { attribute: 'required|acceptedNotIf:1,2,3' }); 41 | 42 | await v.check(); 43 | 44 | throw new Error('Invalid seed exception.'); 45 | } catch (e) { 46 | assert.equal(e, 'Error: Invalid arguments supplied for attribute attribute in acceptedNotIf rule.'); 47 | } 48 | }); 49 | 50 | it('message should exists', async () => { 51 | const v = new Validator( 52 | { attr: 'yes', age: 16 }, 53 | { attr: 'acceptedNotIf:age,16' }, 54 | ); 55 | 56 | const matched = await v.check(); 57 | 58 | assert.equal(matched, false); 59 | 60 | assert.equal( 61 | v.errors.attr.message, 62 | v.getExistinParsedMessage({ 63 | rule: 'acceptedNotIf', 64 | value: 'yes', 65 | attr: 'attr', 66 | args: ['age', '16'], 67 | }), 68 | ); 69 | }); 70 | }); 71 | -------------------------------------------------------------------------------- /test/rules/alphaNumeric.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | const { Validator } = require('../../lib/index'); 4 | 5 | describe('alphaNumeric', () => { 6 | it('should pass with example', async () => { 7 | const v = new Validator( 8 | { username: 'example' }, 9 | { username: 'alphaNumeric' }, 10 | ); 11 | 12 | const matched = await v.check(); 13 | 14 | assert.equal(matched, true); 15 | }); 16 | 17 | it('should pass with alpha-numeric', async () => { 18 | const v = new Validator( 19 | { username: 'now123' }, 20 | { username: 'alphaNumeric' }, 21 | ); 22 | 23 | const matched = await v.check(); 24 | 25 | assert.equal(matched, true); 26 | }); 27 | 28 | it('should fail with special char', async () => { 29 | const v = new Validator( 30 | { username: 'u@name' }, 31 | { username: 'alphaNumeric' }, 32 | ); 33 | 34 | const matched = await v.check(); 35 | 36 | assert.equal(matched, false); 37 | }); 38 | 39 | it('should pass with numbers', async () => { 40 | const v = new Validator( 41 | { username: '123' }, 42 | { username: 'alphaNumeric' }, 43 | ); 44 | 45 | const matched = await v.check(); 46 | 47 | assert.equal(matched, true); 48 | }); 49 | 50 | it('should fail with underscore', async () => { 51 | const v = new Validator( 52 | { username: 'u_name' }, 53 | { username: 'alphaNumeric' }, 54 | ); 55 | 56 | const matched = await v.check(); 57 | 58 | assert.equal(matched, false); 59 | }); 60 | 61 | it('message should exist', async () => { 62 | const v = new Validator( 63 | { username: 'u-name' }, 64 | { username: 'alphaNumeric' }, 65 | ); 66 | 67 | const matched = await v.check(); 68 | 69 | assert.equal(matched, false); 70 | assert.equal( 71 | v.errors.username.message, 72 | v.getExistinParsedMessage({ 73 | rule: 'alphaNumeric', 74 | value: 'u-name', 75 | attr: 'username', 76 | }), 77 | ); 78 | }); 79 | }); 80 | -------------------------------------------------------------------------------- /test/non-bailable.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | const { Validator, bailable } = require('../lib/index'); 4 | 5 | describe('Non bailable', () => { 6 | it('should return multiple errors', async () => { 7 | // bailable(false); 8 | const v = new Validator( 9 | { name: '12' }, 10 | { 11 | name: ['required', ['minLength', '5'], ['maxLength', '10'], 'alpha'], 12 | }, 13 | ); 14 | 15 | v.bail(false); 16 | 17 | const matched = await v.check(); 18 | 19 | assert.equal(matched, false); 20 | assert.equal(Array.isArray(v.errors.name), true); 21 | }); 22 | 23 | it('should toggle multiple errors on current instance', async () => { 24 | // bailable(false); 25 | const v = new Validator( 26 | { name: '12' }, 27 | { 28 | name: ['required', ['minLength', '5'], ['maxLength', '10'], 'alpha'], 29 | }, 30 | ); 31 | 32 | v.bail(false); 33 | 34 | const matched = await v.check(); 35 | 36 | assert.equal(matched, false); 37 | assert.equal(Array.isArray(v.errors.name), true); 38 | 39 | v.bail(true); 40 | 41 | const matchedAgain = await v.check(); 42 | 43 | assert.equal(matchedAgain, false); 44 | assert.equal(!Array.isArray(v.errors.name), true); 45 | }); 46 | 47 | it('should toggle multiple errors', async () => { 48 | // enable multiple errors 49 | bailable(false); 50 | const v = new Validator( 51 | { name: 'art' }, 52 | { 53 | name: ['required', ['minLength', '5'], ['maxLength', '10'], 'alpha'], 54 | }, 55 | ); 56 | 57 | // multiple errors should be enabled 58 | assert.equal(v.breakWhenFailed, false); 59 | 60 | // disable mutliple errors 61 | bailable(true); 62 | 63 | // global disable should not effect pre-created instance 64 | assert.equal(v.breakWhenFailed, false); 65 | 66 | // disable current instance multiple errors 67 | v.bail(true); 68 | 69 | // multiple errors should be turned off 70 | assert.equal(v.breakWhenFailed, true); 71 | }); 72 | }); 73 | -------------------------------------------------------------------------------- /test/rules/min.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | const { Validator } = require('../../lib/index'); 4 | 5 | describe('min', () => { 6 | it('should pass with string', async () => { 7 | const v = new Validator( 8 | { attr: '20' }, 9 | { attr: 'min:20' }, 10 | ); 11 | 12 | const matched = await v.check(); 13 | 14 | assert.equal(matched, true); 15 | }); 16 | 17 | it('should pass with integer', async () => { 18 | const v = new Validator( 19 | { attr: 20 }, 20 | { attr: 'min:20' }, 21 | ); 22 | 23 | const matched = await v.check(); 24 | 25 | assert.equal(matched, true); 26 | }); 27 | 28 | it('should pass with decimal', async () => { 29 | const v = new Validator( 30 | { attr: 20.55 }, 31 | { attr: 'min:20.50' }, 32 | ); 33 | 34 | const matched = await v.check(); 35 | 36 | assert.equal(matched, true); 37 | }); 38 | 39 | it('should fail', async () => { 40 | const v = new Validator( 41 | { attr: '15' }, 42 | { attr: 'min:18' }, 43 | ); 44 | 45 | const matched = await v.check(); 46 | 47 | assert.equal(matched, false); 48 | }); 49 | 50 | it('should throw invalid seed exception', async () => { 51 | try { 52 | const v = new Validator({ attribute: 'Harcharan Singh' }, { attribute: 'required|min:test' }); 53 | 54 | await v.check(); 55 | 56 | throw new Error('Invalid seed exception.'); 57 | } catch (e) { 58 | assert.equal(e, 'Error: Seed in min rule for attribute must be a number.'); 59 | } 60 | }); 61 | 62 | it('message should exist', async () => { 63 | const v = new Validator( 64 | { attr: 'string' }, 65 | { attr: 'min:10' }, 66 | ); 67 | const matched = await v.check(); 68 | 69 | assert.equal(matched, false); 70 | 71 | assert.equal( 72 | v.errors.attr.message, 73 | v.getExistinParsedMessage({ 74 | rule: 'min', 75 | value: 'string', 76 | attr: 'attr', 77 | args: [10], 78 | }), 79 | ); 80 | }); 81 | }); 82 | -------------------------------------------------------------------------------- /test/rules/lte.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | const { Validator } = require('../../lib/index'); 4 | 5 | describe('lte', () => { 6 | it('should pass with greater seed', async () => { 7 | const v = new Validator( 8 | { min: '20', max: '25' }, 9 | { 10 | min: 'required|integer|lte:max', 11 | max: 'required|integer', 12 | }, 13 | ); 14 | 15 | const matched = await v.check(); 16 | 17 | assert.equal(matched, true); 18 | }); 19 | 20 | it('should pass with equal', async () => { 21 | const v = new Validator( 22 | { min: '20', max: '20' }, 23 | { 24 | min: 'required|integer|lte:max', 25 | max: 'required|integer', 26 | }, 27 | ); 28 | 29 | const matched = await v.check(); 30 | 31 | assert.equal(matched, true); 32 | }); 33 | 34 | it('should fail', async () => { 35 | const v = new Validator( 36 | { min: 'abc', max: '25' }, 37 | { 38 | min: 'required|lte:max', 39 | max: 'required|integer', 40 | }, 41 | ); 42 | 43 | const matched = await v.check(); 44 | 45 | assert.equal(matched, false); 46 | }); 47 | 48 | it('should fail due to nan in another field', async () => { 49 | const v = new Validator( 50 | { min: '30', max: 'abc' }, 51 | { 52 | min: 'required|integer|lte:max', 53 | max: 'required', 54 | }, 55 | ); 56 | 57 | const matched = await v.check(); 58 | 59 | assert.equal(matched, false); 60 | }); 61 | 62 | it('message should exist', async () => { 63 | const v = new Validator( 64 | { min: '30', max: 'abc' }, 65 | { 66 | min: 'required|integer|lte:max', 67 | max: 'required', 68 | }, 69 | ); 70 | const matched = await v.check(); 71 | 72 | assert.equal(matched, false); 73 | 74 | assert.equal( 75 | v.errors.min.message, 76 | v.getExistinParsedMessage({ 77 | rule: 'lte', 78 | value: '30', 79 | attr: 'min', 80 | args: ['max'], 81 | }), 82 | ); 83 | }); 84 | }); 85 | -------------------------------------------------------------------------------- /lib/rules/mime.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | const mime = require('mime-types'); 3 | const fileType = require('file-type'); 4 | const readChunk = require('read-chunk'); 5 | 6 | module.exports = async function validateMime({ value, args }) { 7 | const file = value; 8 | 9 | let success = true; 10 | 11 | let mtype; 12 | 13 | if (file.mime) { 14 | mtype = file.mime; 15 | } else if (file.type) { 16 | mtype = file.type; 17 | } else if (file.mimetype) { 18 | mtype = file.mimetype; 19 | } else if (file instanceof Buffer) { 20 | try { 21 | mtype = (await fileType.fromBuffer(file)).mime; 22 | } catch (e) { 23 | /* istanbul ignore next */ 24 | console.error(e); 25 | } 26 | } else if (file.buffer && file.buffer instanceof Buffer) { 27 | try { 28 | mtype = (await fileType.fromBuffer(file.buffer)).mime; 29 | } catch (e) { 30 | /* istanbul ignore next */ 31 | console.error(e); 32 | } 33 | } else if (typeof file === 'string') { 34 | try { 35 | const buffer = await readChunk(file, 0, 4100); 36 | mtype = (await fileType.fromBuffer(buffer)).mime; 37 | } catch (e) { 38 | /* istanbul ignore next */ 39 | console.error(e); 40 | } 41 | } else if (file.path && typeof file.path === 'string') { 42 | try { 43 | const buffer = await readChunk(file.path, 0, 4100); 44 | mtype = (await fileType.fromBuffer(buffer)).mime; 45 | } catch (e) { 46 | /* istanbul ignore next */ 47 | console.error(e); 48 | } 49 | } else { 50 | throw new Error('MIME rule only accepts Buffer,file path or type/mime property in file object.'); 51 | } 52 | 53 | for (let i = 0; i < args.length; ++i) { 54 | if (mime.lookup(args[i]) !== mtype) { 55 | success = false; 56 | } else { 57 | success = true; 58 | break; 59 | } 60 | } 61 | // } else { 62 | // if (mime.lookup(args) !== mtype) { 63 | // success = false; 64 | // } 65 | // } 66 | 67 | if (!success) { 68 | return false; 69 | } 70 | 71 | return true; 72 | }; 73 | -------------------------------------------------------------------------------- /test/rules/gte.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | const { Validator } = require('../../lib/index'); 4 | 5 | describe('gte', () => { 6 | it('should pass with greater seed', async () => { 7 | const v = new Validator( 8 | { min: '20', max: '25' }, 9 | { 10 | min: 'required|integer', 11 | max: 'required|integer|gte:min', 12 | }, 13 | ); 14 | 15 | const matched = await v.check(); 16 | 17 | assert.equal(matched, true); 18 | }); 19 | 20 | it('should pass with equal', async () => { 21 | const v = new Validator( 22 | { min: '20', max: '20' }, 23 | { 24 | min: 'required|integer', 25 | max: 'required|integer|gte:min', 26 | }, 27 | ); 28 | 29 | const matched = await v.check(); 30 | 31 | assert.equal(matched, true); 32 | }); 33 | 34 | it('should fail with min', async () => { 35 | const v = new Validator( 36 | { min: '30', max: '25' }, 37 | { 38 | min: 'required|integer', 39 | max: 'required|integer|gte:min', 40 | }, 41 | ); 42 | 43 | const matched = await v.check(); 44 | 45 | assert.equal(matched, false); 46 | }); 47 | 48 | it('should fail due to nan in another field', async () => { 49 | const v = new Validator( 50 | { min: 'abc', max: '25' }, 51 | { 52 | min: 'required', 53 | max: 'required|integer|gte:min', 54 | }, 55 | ); 56 | 57 | const matched = await v.check(); 58 | 59 | assert.equal(matched, false); 60 | }); 61 | 62 | it('message should exist', async () => { 63 | const v = new Validator( 64 | { min: 'abc', max: 'abc' }, 65 | { 66 | min: 'required', 67 | max: 'required|gte:min', 68 | }, 69 | ); 70 | 71 | const matched = await v.check(); 72 | 73 | assert.equal(matched, false); 74 | assert.equal( 75 | v.errors.max.message, 76 | v.getExistinParsedMessage({ 77 | rule: 'gte', 78 | value: '25', 79 | attr: 'max', 80 | args: ['min'], 81 | }), 82 | ); 83 | }); 84 | }); 85 | -------------------------------------------------------------------------------- /test/rules/requiredNotIf.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | const { Validator } = require('../../lib/index'); 4 | 5 | describe('requiredNotIf', () => { 6 | it('should pass', async () => { 7 | const v = new Validator( 8 | { name: 'Harcharan Singh', age: '16' }, 9 | { sex: 'requiredNotIf:age,16' }, 10 | ); 11 | 12 | const matched = await v.check(); 13 | 14 | assert.equal(matched, true); 15 | }); 16 | 17 | it('should fail', async () => { 18 | const v = new Validator( 19 | { name: 'Harcharan Singh', age: 15, sex: 'male' }, 20 | { sex: 'requiredNotIf:age,16' }, 21 | ); 22 | 23 | const matched = await v.check(); 24 | 25 | assert.equal(matched, false); 26 | }); 27 | 28 | it('should pass with multiple seeds', async () => { 29 | const v = new Validator( 30 | { 31 | name: 'Harcharan Singh', 32 | age: 16, 33 | parent: 'yes', 34 | type: 'subscribed', 35 | // email: 'artisangang@gmail.com' 36 | }, 37 | { 38 | email: 'requiredNotIf:age,16,parent,yes,type,subscribed', 39 | }, 40 | ); 41 | 42 | const matched = await v.check(); 43 | 44 | assert.equal(matched, true); 45 | }); 46 | 47 | it('should fail with multiple seeds', async () => { 48 | const v = new Validator( 49 | { 50 | name: 'Harcharan Singh', 51 | age: 16, 52 | parent: 'yes', 53 | type: 'subscribed', 54 | email: 'artisangang@gmail.com', 55 | }, 56 | { 57 | email: 'requiredNotIf:age,16,parent,yes,type,subscribed', 58 | }, 59 | ); 60 | 61 | const matched = await v.check(); 62 | 63 | assert.equal(matched, false); 64 | }); 65 | 66 | it('message should exist', async () => { 67 | const v = new Validator( 68 | { name: 'Harcharan Singh', age: 15, sex: 'male' }, 69 | { sex: 'requiredNotIf:age,16' }, 70 | ); 71 | 72 | const matched = await v.check(); 73 | assert.equal(matched, false); 74 | 75 | assert.equal( 76 | v.errors.sex.message, 77 | v.getExistinParsedMessage({ 78 | rule: 'requiredNotIf', 79 | value: '', 80 | attr: 'sex', 81 | args: ['age', 16], 82 | }), 83 | ); 84 | }); 85 | }); 86 | -------------------------------------------------------------------------------- /test/rules/acceptedIf.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | const { Validator } = require('../../lib/index'); 4 | 5 | describe('acceptedIf', () => { 6 | it('should pass with yes', async () => { 7 | const v = new Validator( 8 | { attr: 'yes', age: 16 }, 9 | { attr: 'acceptedIf:age,16' }, 10 | ); 11 | 12 | const matched = await v.check(); 13 | 14 | assert.equal(matched, true); 15 | }); 16 | 17 | it('should pass with empty value', async () => { 18 | const v = new Validator( 19 | { attr: '' }, 20 | { attr: 'acceptedIf:age,16' }, 21 | ); 22 | 23 | const matched = await v.check(); 24 | 25 | assert.equal(matched, true); 26 | }); 27 | 28 | it('should fail with no', async () => { 29 | const v = new Validator( 30 | { attr: 'no', age: 16 }, 31 | { attr: 'acceptedIf:age,16' }, 32 | ); 33 | 34 | const matched = await v.check(); 35 | 36 | assert.equal(matched, false); 37 | }); 38 | 39 | it('should throw exception', async () => { 40 | try { 41 | const v = new Validator({ attribute: 'Harcharan Singh' }, { attribute: 'required|acceptedIf' }); 42 | 43 | await v.check(); 44 | 45 | throw new Error('Invalid seed exception.'); 46 | } catch (e) { 47 | assert.equal(e, 'Error: Invalid arguments supplied for field attribute in acceptedIf rule.'); 48 | } 49 | 50 | try { 51 | const v = new Validator({ attribute: 'Harcharan Singh' }, { attribute: 'required|acceptedIf:1,2,3' }); 52 | 53 | await v.check(); 54 | 55 | throw new Error('Invalid seed exception.'); 56 | } catch (e) { 57 | assert.equal(e, 'Error: Invalid arguments supplied for field attribute in acceptedIf rule.'); 58 | } 59 | }); 60 | 61 | it('message should exists', async () => { 62 | const v = new Validator( 63 | { attr: 'no', age: 16 }, 64 | { attr: 'acceptedIf:age,16' }, 65 | ); 66 | 67 | const matched = await v.check(); 68 | 69 | assert.equal(matched, false); 70 | 71 | assert.equal( 72 | v.errors.attr.message, 73 | v.getExistinParsedMessage({ 74 | rule: 'acceptedIf', 75 | value: 'no', 76 | attr: 'attr', 77 | args: ['age', '16'], 78 | }), 79 | ); 80 | }); 81 | }); 82 | -------------------------------------------------------------------------------- /test/edge.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | const { Validator } = require('../lib/index'); 4 | 5 | describe('Edge Cases', () => { 6 | describe('undefined', () => { 7 | it('should ignore undefined and not required fields', async () => { 8 | const v = new Validator({ field: undefined }, { field: 'string' }); 9 | 10 | const matched = await v.check(); 11 | assert.equal(matched, true); 12 | }); 13 | 14 | it('should reject undefined and required fields', async () => { 15 | const v = new Validator({ field: undefined }, { field: 'required|string' }); 16 | 17 | const matched = await v.check(); 18 | assert.equal(matched, false); 19 | }); 20 | }); 21 | 22 | describe('null', () => { 23 | it('should ignore null and not required fields', async () => { 24 | const v = new Validator({ field: null }, { field: 'string' }); 25 | 26 | const matched = await v.check(); 27 | assert.equal(matched, true); 28 | }); 29 | 30 | it('should reject null and required fields', async () => { 31 | const v = new Validator({ field: null }, { 32 | field: 'required|object', 33 | 'field.id': 'required|string' 34 | }); 35 | 36 | const matched = await v.check(); 37 | assert.equal(matched, false); 38 | }); 39 | }); 40 | 41 | describe('empty string', () => { 42 | it('should ignore empty string in not required fields', async () => { 43 | const v = new Validator({ field: '' }, { field: 'string' }); 44 | 45 | const matched = await v.check(); 46 | assert.equal(matched, true); 47 | }); 48 | 49 | it('should reject empty string in required fields', async () => { 50 | const v = new Validator({ field: '' }, { field: 'required|string' }); 51 | 52 | const matched = await v.check(); 53 | assert.equal(matched, false); 54 | }); 55 | }); 56 | 57 | describe('exceptions', () => { 58 | it('Checking for invalid rule', async () => { 59 | try { 60 | const v = new Validator({ name: 'Harcharan Singh' }, { name: 'required|fullName' }); 61 | 62 | await v.check(); 63 | 64 | throw new Error('Rule was missing.'); 65 | } catch (e) { 66 | assert.equal(e, 'Error: Validation Rule: fullName does not exists.'); 67 | } 68 | }); 69 | }); 70 | }); 71 | -------------------------------------------------------------------------------- /test/rules/length.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | const { Validator } = require('../../lib/index'); 4 | 5 | describe('length', () => { 6 | it('should passwith exact len', async () => { 7 | const v = new Validator( 8 | { features: 'abc' }, 9 | { features: 'length:3' }, 10 | ); 11 | 12 | const matched = await v.check(); 13 | 14 | assert.equal(matched, true); 15 | }); 16 | 17 | it('should pass with min len', async () => { 18 | const v = new Validator( 19 | { features: 'abcd' }, 20 | { features: 'length:5,3' }, 21 | ); 22 | 23 | const matched = await v.check(); 24 | 25 | assert.equal(matched, true); 26 | }); 27 | 28 | it('should pass with array of excat len', async () => { 29 | const v = new Validator( 30 | { features: [1, 2, 3] }, 31 | { features: 'length:3' }, 32 | ); 33 | 34 | const matched = await v.check(); 35 | 36 | assert.equal(matched, true); 37 | }); 38 | 39 | it('should pass with array of min length', async () => { 40 | const v = new Validator( 41 | { features: [1, 2, 3, 4] }, 42 | { features: 'length:5,3' }, 43 | ); 44 | 45 | const matched = await v.check(); 46 | 47 | assert.equal(matched, true); 48 | }); 49 | 50 | it('should fail with array of max len', async () => { 51 | const v = new Validator( 52 | { features: [1, 2, 3] }, 53 | { features: 'length:2' }, 54 | ); 55 | 56 | const matched = await v.check(); 57 | 58 | assert.equal(matched, false); 59 | }); 60 | 61 | it('should fail with array of min len', async () => { 62 | const v = new Validator( 63 | { features: [1, 2, 3, 4] }, 64 | { features: 'length:6,5' }, 65 | ); 66 | 67 | const matched = await v.check(); 68 | 69 | assert.equal(matched, false); 70 | }); 71 | 72 | it('message should exist', async () => { 73 | const v = new Validator( 74 | { features: [1, 2, 3, 4] }, 75 | { features: 'length:2,3' }, 76 | ); 77 | 78 | const matched = await v.check(); 79 | 80 | assert.equal(matched, false); 81 | 82 | assert.equal( 83 | v.errors.features.message, 84 | v.getExistinParsedMessage({ 85 | rule: 'length', 86 | value: [1, 2, 3, 4], 87 | attr: 'features', 88 | args: [2, 3], 89 | }), 90 | ); 91 | }); 92 | }); 93 | -------------------------------------------------------------------------------- /test/rules/digits.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | const { Validator } = require('../../lib/index'); 4 | 5 | describe('digits', () => { 6 | it('should pass with digits', async () => { 7 | const v = new Validator( 8 | { attr: '1250' }, 9 | { attr: 'digits:4' }, 10 | ); 11 | 12 | const matched = await v.check(); 13 | 14 | assert.equal(matched, true); 15 | }); 16 | 17 | it('should fail with alpha chars', async () => { 18 | const v = new Validator( 19 | { attr: 'abcd' }, 20 | { attr: 'digits:4' }, 21 | ); 22 | 23 | const matched = await v.check(); 24 | 25 | assert.equal(matched, false); 26 | }); 27 | 28 | it('should fail short length', async () => { 29 | const v = new Validator( 30 | { attr: '123456' }, 31 | { attr: 'digits:8' }, 32 | ); 33 | 34 | const matched = await v.check(); 35 | 36 | assert.equal(matched, false); 37 | }); 38 | 39 | it('should fail with - in numbers', async () => { 40 | const v = new Validator( 41 | { attr: '1234-567' }, 42 | { attr: 'digits:8' }, 43 | ); 44 | 45 | const matched = await v.check(); 46 | 47 | assert.equal(matched, false); 48 | }); 49 | 50 | it('should fail with . in numbers', async () => { 51 | const v = new Validator( 52 | { attr: '120.56' }, 53 | { attr: 'digits:6' }, 54 | ); 55 | 56 | const matched = await v.check(); 57 | 58 | assert.equal(matched, false); 59 | }); 60 | 61 | it('should throw invalid seed exception', async () => { 62 | try { 63 | const v = new Validator({ attribute: 'Harcharan Singh' }, { attribute: 'required|digits:test' }); 64 | 65 | await v.check(); 66 | 67 | throw new Error('Invalid seed exception.'); 68 | } catch (e) { 69 | assert.equal(e, 'Error: Please provide a numeric value for attribute under digits rule.'); 70 | } 71 | }); 72 | 73 | it('message should exist', async () => { 74 | const v = new Validator( 75 | { attr: '123456' }, 76 | { attr: 'digits:5' }, 77 | ); 78 | 79 | const matched = await v.check(); 80 | 81 | assert.equal(matched, false); 82 | assert.equal( 83 | v.errors.attr.message, 84 | v.getExistinParsedMessage({ 85 | rule: 'digits', 86 | value: '123456', 87 | attr: 'attr', 88 | args: [5], 89 | }), 90 | ); 91 | }); 92 | }); 93 | -------------------------------------------------------------------------------- /test/arrayRules.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | const { Validator } = require('../lib/index'); 4 | 5 | describe('Rules as Array', () => { 6 | it('should return false', async () => { 7 | const v = new Validator( 8 | { }, 9 | { 10 | name: ['required'], 11 | }, 12 | ); 13 | 14 | const matched = await v.check(); 15 | 16 | assert.equal(matched, false); 17 | }); 18 | 19 | it('should return true', async () => { 20 | const v = new Validator( 21 | { name: 'artisan' }, 22 | { 23 | name: ['required', ['minLength', '5'], ['maxLength', '10'], 'alpha'], 24 | }, 25 | ); 26 | 27 | const matched = await v.check(); 28 | 29 | // console.log(v.errors); 30 | 31 | assert.equal(matched, true); 32 | }); 33 | 34 | it('should return false due to minLength failed', async () => { 35 | const v = new Validator( 36 | { name: 'art' }, 37 | { 38 | name: ['required', ['minLength', '5'], ['maxLength', '10'], 'alpha'], 39 | }, 40 | ); 41 | 42 | const matched = await v.check(); 43 | 44 | assert.equal(matched, false); 45 | }); 46 | 47 | it('should return false due to lengthBetween failed', async () => { 48 | const v = new Validator( 49 | { uid: 'abcdefghi' }, 50 | { 51 | uid: ['required', ['lengthBetween', '5', '8'], 'alpha'], 52 | }, 53 | ); 54 | const matched = await v.check(); 55 | 56 | assert.equal(matched, false); 57 | }); 58 | 59 | it('regex delimiters fix', async () => { 60 | const v = new Validator( 61 | { uid: 'xyz' }, 62 | { 63 | uid: ['required', ['regex', 'abc|xyz']], 64 | }, 65 | ); 66 | const matched = await v.check(); 67 | 68 | assert.equal(matched, true); 69 | }); 70 | }); 71 | 72 | describe('Rules as Mixed', () => { 73 | it('should return true', async () => { 74 | const v = new Validator( 75 | { 76 | name: 'artisan', 77 | email: 'artisangang@gmail.com', 78 | phone: '+918699987073', 79 | ip: '127.0.0.1', 80 | }, 81 | { 82 | name: ['required', ['minLength', '5'], ['maxLength', '10'], 'alpha'], 83 | email: 'required|email', 84 | ip: ['ip'], 85 | phone: 'required|phoneNumber', 86 | }, 87 | ); 88 | 89 | const matched = await v.check(); 90 | 91 | // console.log(v.errors); 92 | 93 | assert.equal(matched, true); 94 | }); 95 | }); 96 | -------------------------------------------------------------------------------- /test/rules/string.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | const { Validator } = require('../../lib/index'); 4 | 5 | describe('string', () => { 6 | it('should reject integer in string fields', async () => { 7 | const v = new Validator({ atr: 1 }, { atr: 'string' }); 8 | 9 | const matched = await v.check(); 10 | assert.equal(matched, false); 11 | }); 12 | 13 | it('should reject boolean in string fields', async () => { 14 | const v = new Validator({ attr: true }, { attr: 'string' }); 15 | 16 | const matched = await v.check(); 17 | assert.equal(matched, false); 18 | }); 19 | 20 | it('should reject boolean false in string fields', async () => { 21 | const v = new Validator({ attr: false }, { attr: 'string' }); 22 | 23 | const matched = await v.check(); 24 | assert.equal(matched, false); 25 | }); 26 | 27 | it('should reject array in string fields', async () => { 28 | const v = new Validator({ attr: [1, 2] }, { attr: 'string' }); 29 | 30 | const matched = await v.check(); 31 | assert.equal(matched, false); 32 | }); 33 | 34 | it('should reject object in string fields', async () => { 35 | const v = new Validator({ attr: {} }, { attr: 'string' }); 36 | 37 | const matched = await v.check(); 38 | assert.equal(matched, false); 39 | }); 40 | 41 | it('should pass', async () => { 42 | const v = new Validator({ attr: 'string' }, { attr: 'string' }); 43 | 44 | const matched = await v.check(); 45 | assert.equal(matched, true); 46 | }); 47 | 48 | it('should ignore empty string in not required fields', async () => { 49 | const v = new Validator({ attr: '' }, { attr: 'string' }); 50 | 51 | const matched = await v.check(); 52 | assert.equal(matched, true); 53 | }); 54 | 55 | it('should reject empty string in required fields', async () => { 56 | const v = new Validator({ attr: '' }, { attr: 'required|string' }); 57 | 58 | const matched = await v.check(); 59 | assert.equal(matched, false); 60 | }); 61 | 62 | it('message should exist', async () => { 63 | const v = new Validator({ attr: 15.50 }, { attr: 'string' }); 64 | 65 | const matched = await v.check(); 66 | 67 | assert.equal(matched, false); 68 | assert.equal( 69 | v.errors.attr.message, 70 | v.getExistinParsedMessage({ 71 | rule: 'string', 72 | value: '', 73 | attr: 'attr', 74 | args: [], 75 | }), 76 | ); 77 | }); 78 | }); 79 | -------------------------------------------------------------------------------- /test/rules/arrayUniqueObjects.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | const { Validator } = require('../../lib/index'); 4 | 5 | describe('arrayUniqueObjects', () => { 6 | it('should fail with string', async () => { 7 | const v = new Validator( 8 | { features: 'test' }, 9 | { features: 'arrayUniqueObjects:id' }, 10 | ); 11 | 12 | const matched = await v.check(); 13 | 14 | assert.equal(matched, false); 15 | }); 16 | 17 | it('should pass with array of objects', async () => { 18 | const v = new Validator( 19 | { 20 | features: [{ 21 | id: 1, 22 | name: 'ok', 23 | }, 24 | { 25 | id: 2, 26 | name: 'ok2', 27 | }], 28 | }, 29 | { features: 'array|arrayUniqueObjects:id' }, 30 | ); 31 | 32 | const matched = await v.check(); 33 | 34 | assert.equal(matched, true); 35 | }); 36 | 37 | it('should fail for duplicate ids in array of objects', async () => { 38 | const v = new Validator( 39 | { 40 | features: [{ 41 | id: 1, 42 | name: 'ok', 43 | }, 44 | { 45 | id: 1, 46 | name: 'ok2', 47 | }], 48 | }, 49 | { features: 'array|arrayUniqueObjects:id' }, 50 | ); 51 | const matched = await v.check(); 52 | 53 | assert.equal(matched, false); 54 | }); 55 | 56 | it('should pass for multiple individual duplicates and unique when grouped', async () => { 57 | const v = new Validator( 58 | { 59 | features: [{ 60 | id: 1, 61 | name: 'ok', 62 | }, 63 | { 64 | id: 1, 65 | name: 'ok1', 66 | }, 67 | { 68 | id: 3, 69 | name: 'ok2', 70 | }], 71 | }, 72 | { features: 'array|arrayUniqueObjects:id,name' }, 73 | ); 74 | const matched = await v.check(); 75 | 76 | assert.equal(matched, true); 77 | }); 78 | 79 | it('message should exist', async () => { 80 | const v = new Validator( 81 | { features: {} }, 82 | { features: 'arrayUniqueObjects:id' }, 83 | ); 84 | 85 | const matched = await v.check(); 86 | 87 | assert.equal(matched, false); 88 | assert.equal( 89 | v.errors.features.message, 90 | v.getExistinParsedMessage({ 91 | rule: 'arrayUniqueObjects', 92 | value: {}, 93 | attr: 'features', 94 | args: ['id'], 95 | }), 96 | ); 97 | }); 98 | }); 99 | -------------------------------------------------------------------------------- /test/rules/required.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | const { Validator, empty } = require('../../lib/index'); 4 | 5 | describe('#required', () => { 6 | it('should pass', async () => { 7 | const v = new Validator({ name: 'Harcharan Singh' }, { name: 'required' }); 8 | 9 | const matched = await v.check(); 10 | 11 | assert.equal(matched, true); 12 | }); 13 | 14 | it('should pass', async () => { 15 | const v = new Validator({ name: 'Harcharan Singh' }, { name: ['required'] }); 16 | 17 | const matched = await v.check(); 18 | 19 | assert.equal(matched, true); 20 | }); 21 | 22 | it('should fail with empty string', async () => { 23 | const v = new Validator({ name: '' }, { name: 'required' }); 24 | 25 | const matched = await v.check(); 26 | 27 | assert.equal(matched, false); 28 | }); 29 | 30 | it('should pass with spaceed string with modified behaviour', async () => { 31 | const v = new Validator({ name: ' ' }, { name: ['required'] }); 32 | 33 | const matched = await v.check(); 34 | 35 | assert.equal(matched, false); 36 | }); 37 | 38 | it('should pass with 0 as integer', async () => { 39 | const v = new Validator({ name: 0 }, { name: 'required' }); 40 | 41 | const matched = await v.check(); 42 | 43 | assert.equal(matched, true); 44 | }); 45 | 46 | it('should pass with 0 as string', async () => { 47 | const v = new Validator({ name: '0' }, { name: 'required' }); 48 | 49 | const matched = await v.check(); 50 | 51 | assert.equal(matched, true); 52 | }); 53 | 54 | it('should pass with false as boolean', async () => { 55 | const v = new Validator({ name: false }, { name: 'required' }); 56 | 57 | const matched = await v.check(); 58 | 59 | assert.equal(matched, true); 60 | }); 61 | 62 | it('should pass with false as string', async () => { 63 | const v = new Validator({ name: 'false' }, { name: 'required' }); 64 | 65 | const matched = await v.check(); 66 | 67 | assert.equal(matched, true); 68 | }); 69 | 70 | it('message should exist', async () => { 71 | const v = new Validator({ name: '' }, { name: 'required' }); 72 | 73 | const matched = await v.check(); 74 | 75 | assert.equal(matched, false); 76 | assert.equal( 77 | v.errors.name.message, 78 | v.getExistinParsedMessage({ 79 | rule: 'required', 80 | value: '', 81 | attr: 'name', 82 | args: [], 83 | }), 84 | ); 85 | }); 86 | }); 87 | -------------------------------------------------------------------------------- /test/rules/mime.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | const fs = require('fs'); 3 | 4 | const { Validator } = require('../../lib/index'); 5 | 6 | const mime = require('../../lib/rules/mime'); 7 | 8 | describe('mime', () => { 9 | it('should pass', async () => { 10 | const v = new Validator( 11 | { file: fs.readFileSync('./test/stubs/file-small.png') }, { file: 'mime:png,jpg' }, 12 | ); 13 | 14 | const matched = await v.check(); 15 | 16 | assert.equal(matched, true); 17 | }); 18 | 19 | it('should fail, using buffer', async () => { 20 | const v = new Validator( 21 | { file: { buffer: fs.readFileSync('./test/stubs/file-small.png') } }, { file: 'mime:bmp' }, 22 | ); 23 | 24 | const matched = await v.check(); 25 | 26 | assert.equal(matched, false); 27 | }); 28 | 29 | it('should fail, using path', async () => { 30 | const v = new Validator( 31 | { file: { path: './test/stubs/file-small.png' } }, { file: 'mime:gif,bmp' }, 32 | ); 33 | 34 | const matched = await v.check(); 35 | 36 | assert.equal(matched, false); 37 | }); 38 | 39 | it('should fail, path as string', async () => { 40 | const v = new Validator( 41 | { file: './test/stubs/file-small.png' }, { file: 'mime:gif,bmp' }, 42 | ); 43 | 44 | const matched = await v.check(); 45 | 46 | assert.equal(matched, false); 47 | }); 48 | 49 | it('should pass, with manual mime', async () => { 50 | const v = new Validator( 51 | { file: { mime: 'image/gif' } }, { file: 'mime:gif,bmp' }, 52 | ); 53 | 54 | const matched = await v.check(); 55 | 56 | assert.equal(matched, true); 57 | }); 58 | 59 | it('should pass, with manual type', async () => { 60 | const v = new Validator( 61 | { file: { type: 'image/gif' } }, { file: 'mime:gif,bmp' }, 62 | ); 63 | 64 | const matched = await v.check(); 65 | 66 | assert.equal(matched, true); 67 | }); 68 | 69 | it('should pass, with manual mimetype', async () => { 70 | const v = new Validator( 71 | { file: { mimetype: 'image/gif' } }, { file: 'mime:gif,bmp' }, 72 | ); 73 | 74 | const matched = await v.check(); 75 | 76 | assert.equal(matched, true); 77 | }); 78 | 79 | it('should throw exception', async () => { 80 | try { 81 | await mime({ value: {}, args: ['png'] }); 82 | } catch (e) { 83 | assert.equal(e, 'Error: MIME rule only accepts Buffer,file path or type/mime property in file object.'); 84 | } 85 | }); 86 | }); 87 | -------------------------------------------------------------------------------- /test/rules/sometimes.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | const { Validator } = require('../../lib/index'); 4 | 5 | describe('sometimes', () => { 6 | it('should fail', async () => { 7 | const v = new Validator( 8 | { password: '', confirm_password: 'password' }, 9 | { password: 'sometimes', confirm_password: 'sometimes|alpha' }, 10 | ); 11 | 12 | const matched = await v.check(); 13 | 14 | assert.equal(matched, false); 15 | }); 16 | 17 | it('should pass', async () => { 18 | const v = new Validator( 19 | { password: '000000', confirm_password: '000000' }, 20 | { password: 'sometimes', confirm_password: 'sometimes|same:password' }, 21 | ); 22 | 23 | const matched = await v.check(); 24 | 25 | assert.equal(matched, true); 26 | }); 27 | 28 | it('should pass, attribute absent', async () => { 29 | const v = new Validator( 30 | {}, 31 | { password: 'sometimes', confirm_password: 'sometimes|same:password' }, 32 | ); 33 | 34 | const matched = await v.check(); 35 | 36 | assert.equal(matched, true); 37 | }); 38 | 39 | it('message should exist', async () => { 40 | const v = new Validator( 41 | { password: '', confirm_password: 'password' }, 42 | { password: 'sometimes', confirm_password: 'sometimes|alpha' }, 43 | ); 44 | 45 | const matched = await v.check(); 46 | 47 | assert.equal(matched, false); 48 | assert.equal( 49 | v.errors.password.message, 50 | v.getExistinParsedMessage({ 51 | rule: 'sometimes', 52 | value: '', 53 | attr: 'password', 54 | args: [], 55 | }), 56 | ); 57 | }); 58 | 59 | it('should fail', async () => { 60 | const v = new Validator( 61 | { info: { password: '', confirm_password: 'password' } }, 62 | { 'info.password': 'sometimes', 'info.confirm_password': 'sometimes|alpha' }, 63 | ); 64 | 65 | const matched = await v.check(); 66 | 67 | assert.equal(matched, false); 68 | }); 69 | 70 | it('should pass', async () => { 71 | const v = new Validator( 72 | { info: { password: 'password', confirm_password: 'password' } }, 73 | { 'info.password': 'sometimes', 'info.confirm_password': 'sometimes|alpha' }, 74 | ); 75 | 76 | const matched = await v.check(); 77 | assert.equal(matched, true); 78 | }); 79 | 80 | it('should pass, attribute absent', async () => { 81 | const v = new Validator( 82 | {}, 83 | { 'info.password': 'sometimes', 'info.confirm_password': 'sometimes|alpha' }, 84 | ); 85 | 86 | const matched = await v.check(); 87 | 88 | assert.equal(matched, true); 89 | }); 90 | }); 91 | -------------------------------------------------------------------------------- /test/rules/alphaDash.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | const { Validator } = require('../../lib/index'); 4 | 5 | describe('alphaDash', () => { 6 | it('should pass with example', async () => { 7 | const v = new Validator( 8 | { username: 'example' }, 9 | { username: 'alphaDash' }, 10 | ); 11 | 12 | const matched = await v.check(); 13 | 14 | assert.equal(matched, true); 15 | }); 16 | 17 | it('should pass with example-test', async () => { 18 | const v = new Validator( 19 | { username: 'example-test' }, 20 | { username: 'alphaDash' }, 21 | ); 22 | 23 | const matched = await v.check(); 24 | 25 | assert.equal(matched, true); 26 | }); 27 | 28 | it('should pass with alpha-numeric value', async () => { 29 | const v = new Validator( 30 | { username: 'now123' }, 31 | { username: 'alphaDash' }, 32 | ); 33 | 34 | const matched = await v.check(); 35 | 36 | assert.equal(matched, true); 37 | }); 38 | 39 | it('should pass with numbers', async () => { 40 | const v = new Validator( 41 | { username: '123' }, 42 | { username: 'alphaDash' }, 43 | ); 44 | 45 | const matched = await v.check(); 46 | 47 | assert.equal(matched, true); 48 | }); 49 | 50 | it('should pass with alpha, dash and numbers', async () => { 51 | const v = new Validator( 52 | { username: 'now-123' }, 53 | { username: 'alphaDash' }, 54 | ); 55 | 56 | const matched = await v.check(); 57 | 58 | assert.equal(matched, true); 59 | }); 60 | 61 | it('should pass with underscore', async () => { 62 | const v = new Validator( 63 | { username: 'u_name' }, 64 | { username: 'alphaDash' }, 65 | ); 66 | 67 | const matched = await v.check(); 68 | 69 | assert.equal(matched, true); 70 | }); 71 | 72 | it('should fail with special char', async () => { 73 | const v = new Validator( 74 | { username: 'u@name' }, 75 | { username: 'alphaDash' }, 76 | ); 77 | 78 | const matched = await v.check(); 79 | 80 | assert.equal(matched, false); 81 | }); 82 | 83 | it('message should exist', async () => { 84 | const v = new Validator( 85 | { username: 'u+name' }, 86 | { username: 'alphaDash' }, 87 | ); 88 | 89 | const matched = await v.check(); 90 | 91 | assert.equal(matched, false); 92 | assert.equal( 93 | v.errors.username.message, 94 | v.getExistinParsedMessage({ 95 | rule: 'alphaDash', 96 | value: 'u+name', 97 | attr: 'username', 98 | }), 99 | ); 100 | }); 101 | }); 102 | -------------------------------------------------------------------------------- /test/rootArray.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | const { Validator } = require('../lib/index'); 3 | 4 | describe('Root Level Array', () => { 5 | it('should pass with array as root level', async () => { 6 | const v = new Validator( 7 | [ 8 | { field: 'admin@example.com' }, 9 | ], 10 | { '*.field': 'required|email' }, 11 | ); 12 | 13 | const matched = await v.check(); 14 | assert.equal(matched, true); 15 | }); 16 | 17 | it('should fail with array as root level', async () => { 18 | const v = new Validator( 19 | [ 20 | { field: 'string' }, 21 | ], 22 | { '*.field': 'required|email' }, 23 | ); 24 | 25 | const matched = await v.check(); 26 | assert.equal(matched, false); 27 | v.errors.should.have.key('0.field'); 28 | }); 29 | 30 | it('should pass with array as root level contains nested object', async () => { 31 | const v = new Validator([ 32 | { 33 | field: { 34 | email: 'admin@example.com' 35 | }, 36 | }, 37 | ], 38 | { '*.field.email': 'required|email' },); 39 | 40 | const matched = await v.check(); 41 | assert.equal(matched, true); 42 | }); 43 | 44 | it('should fail with array as root level contains nested object', async () => { 45 | const v = new Validator( 46 | [ 47 | { 48 | field: { 49 | email: 'string' 50 | }, 51 | }, 52 | ], 53 | { '*.field.email': 'required|email' }, 54 | ); 55 | const matched = await v.check(); 56 | assert.equal(matched, false); 57 | v.errors.should.have.key('0.field.email'); 58 | }); 59 | 60 | it('should pass with array as root level contains nested object', async () => { 61 | const v = new Validator( 62 | [ 63 | { 64 | field: { 65 | mails: [ 66 | { 67 | email: 'admin@example.com' 68 | } 69 | ], 70 | }, 71 | }, 72 | ], 73 | { '*.field.mails.*.email': 'required|email' } 74 | ); 75 | 76 | const matched = await v.check(); 77 | assert.equal(matched, true); 78 | }); 79 | 80 | it('should fail with array as root level contains nested object', async () => { 81 | const v = new Validator( 82 | [ 83 | { 84 | field: { 85 | mails: [ 86 | { 87 | email: 'admin@example.com' 88 | }, 89 | { 90 | email: 'string' 91 | }, 92 | { 93 | email: 'admin@example.com' 94 | } 95 | ], 96 | }, 97 | }, 98 | ], 99 | { '*.field.mails.*.email': 'required|email' } 100 | ); 101 | 102 | const matched = await v.check(); 103 | assert.equal(matched, false); 104 | v.errors.should.have.key('0.field.mails.1.email'); 105 | }); 106 | }); 107 | -------------------------------------------------------------------------------- /test/util.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | const str = require('../lib/util/str'); 3 | const namedArgs = require('../lib/util/namedArgs'); 4 | const obj = require('../lib/util/obj'); 5 | const niv = require('../lib/index'); 6 | 7 | describe('util:No in use', () => { 8 | describe('str:trim', () => { 9 | it('should trim dot', async () => { 10 | const out = str.trim('.cart.', '.'); 11 | assert.equal(out, 'cart'); 12 | }); 13 | }); 14 | }); 15 | 16 | describe('util:in use', () => { 17 | describe('namedArgs', () => { 18 | it('should return empty object', async () => { 19 | const out = namedArgs(null); 20 | assert.equal(JSON.stringify(out), JSON.stringify({})); 21 | }); 22 | }); 23 | describe('obj:strNotations', () => { 24 | it('should return keys array', async () => { 25 | const out = obj.strNotations({ 26 | cart: [ 27 | { 28 | products: [ 29 | { 30 | 'p.ids': [1, 2, 3], 31 | }, 32 | ], 33 | }, 34 | ], 35 | }, { repetition: 10, values: false }); 36 | 37 | if (!Array.isArray(out)) { 38 | throw new Error('Array was expected.'); 39 | } 40 | }); 41 | 42 | it('should use custom escaper', async () => { 43 | const out = obj.strNotations( 44 | { 45 | cart: [ 46 | { 47 | 'products.ids': [1, 2, 3], 48 | }, 49 | ], 50 | }, 51 | { 52 | repetition: 10, 53 | values: false, 54 | escape: (key, options) => key.split(options.seperator).join(`\\.${options.seperator}`), 55 | }, 56 | ); 57 | 58 | if (!Array.isArray(out)) { 59 | throw new Error('Array was expected.'); 60 | } 61 | }); 62 | 63 | it('should throw max rep exception', async () => { 64 | obj.setStrNotationRepetition(2); 65 | try { 66 | obj.strNotations({ 67 | cart: [ 68 | { 69 | products: [ 70 | { 71 | 'p.ids': [1, 2, 3], 72 | }, 73 | ], 74 | }, 75 | ], 76 | }, { values: false }); 77 | 78 | throw new Error('Should throw max repetition reached.'); 79 | } catch (e) { 80 | assert.strictEqual(e.message, 'Max(2) repetation was reached.'); 81 | } 82 | }); 83 | 84 | it('should throw max rep exception', async () => { 85 | niv.setStrNotationRepetition(3); 86 | try { 87 | obj.strNotations({ 88 | cart: [ 89 | { 90 | products: [ 91 | { 92 | 'p.ids': [1, 2, 3], 93 | }, 94 | ], 95 | }, 96 | ], 97 | }, { values: false }); 98 | 99 | throw new Error('Should throw max repetition reached.'); 100 | } catch (e) { 101 | assert.strictEqual(e.message, 'Max(3) repetation was reached.'); 102 | } 103 | }); 104 | }); 105 | }); 106 | -------------------------------------------------------------------------------- /lib/rules/index.js: -------------------------------------------------------------------------------- 1 | exports.accepted = require('./accepted'); 2 | exports.acceptedIf = require('./acceptedIf'); 3 | exports.acceptedNotIf = require('./acceptedNotIf'); 4 | exports.after = require('./after'); 5 | exports.alpha = require('./alpha'); 6 | exports.alphaDash = require('./alphaDash'); 7 | exports.alphaNumeric = require('./alphaNumeric'); 8 | exports.array = require('./array'); 9 | exports.arrayUnique = require('./arrayUnique'); 10 | exports.arrayUniqueObjects = require('./arrayUniqueObjects'); 11 | exports.ascii = require('./ascii'); 12 | exports.base64 = require('./base64'); 13 | exports.before = require('./before'); 14 | exports.between = require('./between'); 15 | exports.boolean = require('./boolean'); 16 | exports.contains = require('./contains'); 17 | exports.creditCard = require('./creditCard'); 18 | exports.date = require('./date'); 19 | exports.dateFormat = require('./dateFormat'); 20 | exports.datetime = require('./datetime'); 21 | exports.dateiso = require('./dateiso'); 22 | exports.dateAfter = require('./dateAfter'); 23 | exports.dateAfterToday = require('./dateAfterToday'); 24 | exports.dateBeforeToday = require('./dateBeforeToday'); 25 | exports.dateBefore = require('./dateBefore'); 26 | exports.decimal = require('./decimal'); 27 | exports.different = require('./different'); 28 | exports.digits = require('./digits'); 29 | exports.digitsBetween = require('./digitsBetween'); 30 | exports.dimensions = require('./dimensions'); 31 | exports.domain = require('./domain'); 32 | exports.email = require('./email'); 33 | exports.equals = require('./equals'); 34 | exports.gt = require('./gt'); 35 | exports.gte = require('./gte'); 36 | exports.length = require('./length'); 37 | exports.lt = require('./lt'); 38 | exports.lte = require('./lte'); 39 | exports.hash = require('./hash'); 40 | exports.hex = require('./hex'); 41 | exports.hexColor = require('./hexColor'); 42 | exports.in = require('./in'); 43 | exports.integer = require('./integer'); 44 | exports.ip = require('./ip'); 45 | exports.iso8601 = require('./iso8601'); 46 | exports.json = require('./json'); 47 | exports.latLong = require('./latLong'); 48 | exports.lengthBetween = require('./lengthBetween'); 49 | exports.macAddress = require('./macAddress'); 50 | exports.max = require('./max'); 51 | exports.maxLength = require('./maxLength'); 52 | exports.mime = require('./mime'); 53 | exports.min = require('./min'); 54 | exports.minLength = require('./minLength'); 55 | exports.mongoId = require('./mongoId'); 56 | exports.notContains = require('./notContains'); 57 | exports.notIn = require('./notIn'); 58 | // exports.nullable = require('./nullable'); 59 | exports.numeric = require('./numeric'); 60 | exports.object = require('./object'); 61 | exports.phoneNumber = require('./phoneNumber'); 62 | exports.regex = require('./regex'); 63 | exports.required = require('./required'); 64 | exports.requiredIf = require('./requiredIf'); 65 | exports.requiredNotIf = require('./requiredNotIf'); 66 | exports.requiredWith = require('./requiredWith'); 67 | exports.requiredWithout = require('./requiredWithout'); 68 | exports.same = require('./same'); 69 | exports.size = require('./size'); 70 | exports.sometimes = require('./sometimes'); 71 | exports.string = require('./string'); 72 | exports.url = require('./url'); 73 | -------------------------------------------------------------------------------- /test/rules/size.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | const fs = require('fs'); 3 | 4 | const { Validator } = require('../../lib/index'); 5 | 6 | describe('size', () => { 7 | it('should pass with buffer', async () => { 8 | const v = new Validator( 9 | { file: fs.readFileSync('./test/stubs/file-small.png') }, { file: 'size:4kb' }, 10 | ); 11 | 12 | const matched = await v.check(); 13 | 14 | assert.equal(matched, true); 15 | }); 16 | 17 | it('should pass with buffer using min and max seed', async () => { 18 | const v = new Validator( 19 | { file: fs.readFileSync('./test/stubs/file-small.png') }, { file: 'size:4kb,2kb' }, 20 | ); 21 | 22 | const matched = await v.check(); 23 | 24 | assert.equal(matched, true); 25 | }); 26 | 27 | it('should fail min size', async () => { 28 | const v = new Validator( 29 | { file: fs.readFileSync('./test/stubs/file-small.png') }, { file: 'size:5kb,4kb' }, 30 | ); 31 | 32 | const matched = await v.check(); 33 | 34 | assert.equal(matched, false); 35 | }); 36 | 37 | it('should fail max size', async () => { 38 | const v = new Validator( 39 | { file: fs.readFileSync('./test/stubs/file-small.png') }, { file: 'size:,4kb' }, 40 | ); 41 | 42 | const matched = await v.check(); 43 | 44 | assert.equal(matched, false); 45 | }); 46 | 47 | it('should pass with path', async () => { 48 | const v = new Validator( 49 | { file: { path: './test/stubs/file-small.png' } }, { file: 'size:4kb' }, 50 | ); 51 | 52 | const matched = await v.check(); 53 | 54 | assert.equal(matched, true); 55 | }); 56 | 57 | it('should fails', async () => { 58 | const v = new Validator( 59 | { file: { buffer: fs.readFileSync('./test/stubs/file-small.png') } }, { file: 'size:1kb' }, 60 | ); 61 | 62 | const matched = await v.check(); 63 | 64 | assert.equal(matched, false); 65 | }); 66 | 67 | it('should faile with path', async () => { 68 | const v = new Validator( 69 | { file: './test/stubs/file-small.png' }, { file: 'size:1kb' }, { 'file.size': 'Max 1kb size is allowed.' }, 70 | ); 71 | 72 | const matched = await v.check(); 73 | 74 | assert.equal(matched, false); 75 | }); 76 | 77 | it('should faile with path', async () => { 78 | const v = new Validator( 79 | { file: { size: 2300 } }, { file: 'size:5kb,4kb' }, 80 | ); 81 | 82 | const matched = await v.check(); 83 | 84 | assert.equal(matched, false); 85 | }); 86 | 87 | it('should throw exception invalid context', async () => { 88 | try { 89 | const v = new Validator( 90 | { file: { ok: true } }, { file: 'size:1kb' }, 91 | ); 92 | 93 | await v.check(); 94 | throw new Error('Invalid context.'); 95 | } catch (e) { 96 | assert.equal(e, 'Error: Size rule only accepts Buffer,file path or size property in file object.'); 97 | } 98 | }); 99 | 100 | it('message should exist', async () => { 101 | const v = new Validator( 102 | { file: './test/stubs/file-small.png' }, { file: 'size:1kb' }, 103 | ); 104 | 105 | const matched = await v.check(); 106 | 107 | assert.equal(matched, false); 108 | assert.equal( 109 | v.errors.file.message, 110 | v.getExistinParsedMessage({ 111 | rule: 'size', 112 | value: '', 113 | attr: 'file', 114 | args: ['1kb'], 115 | }), 116 | ); 117 | }); 118 | }); 119 | -------------------------------------------------------------------------------- /test/rules/lengthBetween.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | const { Validator } = require('../../lib/index'); 4 | 5 | describe('lengthBetween', () => { 6 | it('should pass with string', async () => { 7 | const v = new Validator( 8 | { age: 'unamea' }, 9 | { age: 'lengthBetween:5,10' }, 10 | ); 11 | 12 | const matched = await v.check(); 13 | 14 | assert.equal(matched, true); 15 | }); 16 | 17 | it('should fail min length', async () => { 18 | const v = new Validator( 19 | { age: 'name' }, 20 | { age: 'lengthBetween:5,21' }, 21 | ); 22 | 23 | const matched = await v.check(); 24 | 25 | assert.equal(matched, false); 26 | }); 27 | 28 | it('should pass with array', async () => { 29 | const v = new Validator( 30 | { features: [1, 2, 3, 4] }, 31 | { features: 'lengthBetween:3,5' }, 32 | ); 33 | 34 | const matched = await v.check(); 35 | 36 | assert.equal(matched, true); 37 | }); 38 | 39 | it('should fail with array min len', async () => { 40 | const v = new Validator( 41 | { features: [1, 2] }, 42 | { features: 'lengthBetween:3,6' }, 43 | ); 44 | 45 | const matched = await v.check(); 46 | 47 | assert.equal(matched, false); 48 | }); 49 | 50 | it('should throw invalid seed count exception', async () => { 51 | try { 52 | const v = new Validator({ attribute: 'Harcharan Singh' }, { attribute: 'required|lengthBetween:a' }); 53 | 54 | await v.check(); 55 | 56 | throw new Error('Invalid seed exception.'); 57 | } catch (e) { 58 | assert.equal(e, 'Error: The number of arguments for length between in the field attribute are invalid.'); 59 | } 60 | }); 61 | 62 | it('should throw invalid min seed exception', async () => { 63 | try { 64 | const v = new Validator({ attribute: 'Harcharan Singh' }, { attribute: 'required|lengthBetween:a,10' }); 65 | 66 | await v.check(); 67 | 68 | throw new Error('Invalid seed exception.'); 69 | } catch (e) { 70 | assert.equal(e, 'Error: Seeds must be integer for lengthBetween rule.'); 71 | } 72 | }); 73 | 74 | it('should throw invalid max seed exception', async () => { 75 | try { 76 | const v = new Validator({ attribute: 'Harcharan Singh' }, { attribute: 'required|lengthBetween:10,b' }); 77 | 78 | await v.check(); 79 | 80 | throw new Error('Invalid seed exception.'); 81 | } catch (e) { 82 | assert.equal(e, 'Error: Seeds must be integer for lengthBetween rule.'); 83 | } 84 | }); 85 | 86 | it('should throw min must be less then max seed exception', async () => { 87 | try { 88 | const v = new Validator({ attribute: '789456' }, { attribute: 'required|lengthBetween:10,5' }); 89 | 90 | await v.check(); 91 | 92 | throw new Error('Invalid seed exception.'); 93 | } catch (e) { 94 | assert.equal(e, 'Error: Seed min must be less then max in lengthBetween.'); 95 | } 96 | }); 97 | 98 | it('message should exist', async () => { 99 | const v = new Validator( 100 | { features: {} }, 101 | { features: 'lengthBetween:2,3' }, 102 | ); 103 | const matched = await v.check(); 104 | 105 | assert.equal(matched, false); 106 | 107 | assert.equal( 108 | v.errors.features.message, 109 | v.getExistinParsedMessage({ 110 | rule: 'lengthBetween', 111 | value: [1, 2, 3, 4, 5], 112 | attr: 'features', 113 | args: [2, 3], 114 | }), 115 | ); 116 | }); 117 | }); 118 | -------------------------------------------------------------------------------- /test/rules/digitsBetween.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | const { Validator } = require('../../lib/index'); 4 | 5 | describe('digitsBetween', () => { 6 | it('should pass with min length', async () => { 7 | const v = new Validator( 8 | { attr: '1250' }, 9 | { attr: 'digitsBetween:4,6' }, 10 | ); 11 | 12 | const matched = await v.check(); 13 | 14 | assert.equal(matched, true); 15 | }); 16 | 17 | it('should pass with max length', async () => { 18 | const v = new Validator( 19 | { attr: '125012' }, 20 | { attr: 'digitsBetween:4,6' }, 21 | ); 22 | 23 | const matched = await v.check(); 24 | 25 | assert.equal(matched, true); 26 | }); 27 | 28 | it('should fail with min length', async () => { 29 | const v = new Validator( 30 | { attr: '1' }, 31 | { attr: 'digitsBetween:2,3' }, 32 | ); 33 | 34 | const matched = await v.check(); 35 | 36 | assert.equal(matched, false); 37 | }); 38 | 39 | it('should fail with max length', async () => { 40 | const v = new Validator( 41 | { attr: '123456' }, 42 | { attr: 'digitsBetween:2,3' }, 43 | ); 44 | 45 | const matched = await v.check(); 46 | 47 | assert.equal(matched, false); 48 | }); 49 | 50 | it('should throw seed count exception', async () => { 51 | try { 52 | const v = new Validator({ attribute: '789456' }, { attribute: 'required|digitsBetween:a' }); 53 | 54 | await v.check(); 55 | 56 | throw new Error('Invalid seed exception.'); 57 | } catch (e) { 58 | assert.equal(e, 'Error: The number of arguments for digitsBetween rule in the field attribute are invalid.'); 59 | } 60 | }); 61 | 62 | it('should throw invalid min seed exception', async () => { 63 | try { 64 | const v = new Validator({ attribute: 'Harcharan Singh' }, { attribute: 'required|digitsBetween:a,10' }); 65 | 66 | await v.check(); 67 | 68 | throw new Error('Invalid seed exception.'); 69 | } catch (e) { 70 | assert.equal(e, 'Error: Seeds must be integer for attribute under digitsBetween rule.'); 71 | } 72 | }); 73 | 74 | it('should throw invalid max seed exception', async () => { 75 | try { 76 | const v = new Validator({ attribute: 'Harcharan Singh' }, { attribute: 'required|digitsBetween:10,b' }); 77 | 78 | await v.check(); 79 | 80 | throw new Error('Invalid seed exception.'); 81 | } catch (e) { 82 | assert.equal(e, 'Error: Seeds must be integer for attribute under digitsBetween rule.'); 83 | } 84 | }); 85 | 86 | it('should throw min must be less then max seed exception', async () => { 87 | try { 88 | const v = new Validator({ attribute: '789456123' }, { attribute: 'required|digitsBetween:10,5' }); 89 | 90 | await v.check(); 91 | 92 | throw new Error('Invalid seed exception.'); 93 | } catch (e) { 94 | assert.equal(e, 'Error: Seed min must be less then max in digitsBetween rule for attribute.'); 95 | } 96 | }); 97 | 98 | it('message should exist', async () => { 99 | const v = new Validator( 100 | { attr: 'asdfd' }, 101 | { attr: 'digitsBetween:2,3' }, 102 | ); 103 | 104 | const matched = await v.check(); 105 | 106 | assert.equal(matched, false); 107 | assert.equal( 108 | v.errors.attr.message, 109 | v.getExistinParsedMessage({ 110 | rule: 'digitsBetween', 111 | value: 'asdfd', 112 | attr: 'attr', 113 | args: [2, 3], 114 | }), 115 | ); 116 | }); 117 | }); 118 | -------------------------------------------------------------------------------- /test/rules/requiredWith.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | const { Validator } = require('../../lib/index'); 4 | 5 | describe('#requiredWith', () => { 6 | it('should return false for missing seed length', async () => { 7 | try { 8 | const v = new Validator({ age: 16 }, { remember: 'requiredWith' }); 9 | 10 | await v.check(); 11 | throw new Error('Exception was excepted.'); 12 | } catch (e) { 13 | assert.equal(e, 'Error: Invalid arguments supplied for field remember in required with rule.'); 14 | } 15 | 16 | // assert.equal(v.errors.remember.message, v.parseExistingMessageOnly('requiredIf', 'remember', '', ['age', '16'])); 17 | }); 18 | 19 | it('should pass', async () => { 20 | // validate with single seed 21 | const v = new Validator( 22 | { 23 | name: 'Harcharan Singh', sex: 'male', email: '', ip: '', 24 | }, 25 | { email: 'email', ip: 'requiredWith:email|ip' }, 26 | ); 27 | 28 | const matched = await v.check(); 29 | 30 | assert.equal(matched, true); 31 | }); 32 | it('should pass', async () => { 33 | // validate with single seed 34 | const v = new Validator( 35 | { 36 | name: 'Harcharan Singh', sex: 'male', address: { street: 'fantastic' }, ip: '', 37 | }, 38 | { email: 'email', sex: 'requiredWith:address.street' }, 39 | ); 40 | 41 | const matched = await v.check(); 42 | 43 | assert.equal(matched, true); 44 | }); 45 | 46 | it('should fail', async () => { 47 | // validate with multiple seeds 48 | const v = new Validator( 49 | { 50 | name: 'Harcharan Singh', sex: 'male', email: '', ip: '', 51 | }, 52 | { email: 'requiredWith:name,sex' }, 53 | ); 54 | 55 | const matched = await v.check(); 56 | 57 | assert.equal(matched, false); 58 | }); 59 | it('should fail', async () => { 60 | // validate with multiple seeds 61 | const v = new Validator( 62 | { 63 | name: 'Harcharan Singh', address: { street: 'fantastic' }, email: '', ip: '', 64 | }, 65 | { email: 'requiredWith:name,address.street' }, 66 | ); 67 | 68 | const matched = await v.check(); 69 | 70 | assert.equal(matched, false); 71 | }); 72 | 73 | it('should pass', async () => { 74 | // validate with multiple seeds 75 | const v = new Validator( 76 | { 77 | name: 'Harcharan Singh', sex: 'male', email: 'artisangang@gmail.com', ip: '', 78 | }, 79 | { email: 'requiredWith:name,sex' }, 80 | ); 81 | 82 | const matched = await v.check(); 83 | 84 | assert.equal(matched, true); 85 | }); 86 | 87 | it('should fail', async () => { 88 | // check for fails 89 | const v = new Validator( 90 | { 91 | name: 'Harcharan Singh', sex: 'male', email: 'artisangang@gmail.com', ip: '', 92 | }, 93 | { email: 'email', ip: 'requiredWith:email|ip' }, 94 | ); 95 | 96 | const matched = await v.check(); 97 | assert.equal(matched, false); 98 | }); 99 | 100 | it('message should exist', async () => { 101 | const v = new Validator( 102 | { 103 | name: 'Harcharan Singh', sex: 'male', email: 'artisangang@gmail.com', ip: '', 104 | }, 105 | { email: 'email', ip: 'requiredWith:email|ip' }, 106 | ); 107 | 108 | const matched = await v.check(); 109 | 110 | assert.equal(matched, false); 111 | 112 | assert.equal( 113 | v.errors.ip.message, 114 | v.getExistinParsedMessage({ 115 | rule: 'requiredWith', 116 | value: '', 117 | attr: 'ip', 118 | args: ['email'], 119 | }), 120 | ); 121 | }); 122 | }); 123 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | const messages = require('./messages/index'); 2 | const rules = require('./rules/index'); 3 | const Validator = require('./validator'); 4 | const objUtil = require('./util/obj'); 5 | const empty = require('./util/empty'); 6 | 7 | /** 8 | * set default language 9 | * @param {*} lang 10 | */ 11 | function setLang(lang) { 12 | messages.defaultLang = lang; 13 | } 14 | 15 | function extend(name, callback, isImplicit = false) { 16 | rules[name] = callback; 17 | if (isImplicit) Validator.addImplicitRule(name); 18 | } 19 | 20 | /** 21 | * extend/update validation rule default messages 22 | * @param {Object} newMessages 23 | * @param {string} lang 24 | */ 25 | function extendMessages(newMessages, lang = 'en') { 26 | if (typeof messages[lang] === 'undefined') { 27 | messages[lang] = {}; 28 | } 29 | messages[lang] = Object.assign(messages[lang], newMessages); 30 | } 31 | 32 | /** 33 | * add/update your own custom validation messages 34 | * @param {*} customMessages 35 | * @param {*} lang 36 | */ 37 | function addCustomMessages(customMessages, lang = 'en') { 38 | if (typeof messages[lang] === 'undefined') { 39 | messages[lang] = {}; 40 | } 41 | messages[lang].$custom = Object.assign(messages[lang].$custom || {}, customMessages); 42 | } 43 | 44 | function niceNames(attributes, lang = 'en') { 45 | messages[lang].$niceNames = Object.assign(messages[lang].$niceNames || {}, attributes); 46 | } 47 | 48 | /* istanbul ignore next */ 49 | function koa() { 50 | return async (ctx, next) => { 51 | // @ts-ignore 52 | ctx.validationErrors = function validationErrors(errors) { 53 | return { 54 | body: { 55 | message: 'The given data is invalid.', 56 | errors, 57 | }, 58 | }; 59 | }; 60 | 61 | ctx.validate = async function validate(rulesArray, inputs, useMessages) { 62 | const v = new Validator( 63 | inputs || { ...this.request.body, ...this.request.files }, 64 | rulesArray || {}, 65 | useMessages || {}, 66 | ); 67 | 68 | if (await v.fails()) { 69 | this.throw(422, this.validationErrors(v.errors)); 70 | } 71 | 72 | return v; 73 | }; 74 | 75 | ctx.validator = (inputs, rulesArray, useMessages) => new Validator( 76 | inputs || { ...this.request.body, ...this.request.files }, 77 | rulesArray || {}, 78 | useMessages || {}, 79 | ); 80 | 81 | try { 82 | await next(); 83 | } catch (err) { 84 | if (err.status && err.status === 422) { 85 | ctx.type = 'json'; 86 | ctx.status = 422; 87 | ctx.body = err.body; 88 | return; 89 | } 90 | throw err; 91 | } 92 | }; 93 | } 94 | 95 | /** 96 | * enable/disable multiple errors output 97 | * @param {boolean} sure 98 | */ 99 | function bailable(sure) { 100 | Validator.bailable(sure); 101 | } 102 | 103 | /** 104 | * @beta 105 | * enable/disable collection of inputs as per declared rules 106 | * when enabled, validated inputs can be retrieved via data() method 107 | * @param {boolean} enable 108 | */ 109 | function collectInputs(enable) { 110 | Validator.collectInputs(enable); 111 | } 112 | 113 | function assert(validationRules) { 114 | Object.keys(validationRules).forEach((key) => { 115 | const rawRules = validationRules[key]; 116 | let arrayRules = []; 117 | if (Array.isArray(rawRules)) { 118 | arrayRules = rawRules; 119 | } else { 120 | rawRules.split('|').forEach((raw) => { 121 | const [ruleName] = raw.split(':'); 122 | arrayRules.push(ruleName); 123 | }); 124 | } 125 | arrayRules.forEach((rule) => { 126 | if (typeof rules[rule] !== 'function') { 127 | throw new Error(`Rule ${rule} used for attribute ${key} is invalid.`); 128 | } 129 | }); 130 | }); 131 | 132 | return validationRules; 133 | } 134 | 135 | module.exports = { 136 | Validator, 137 | setLang, 138 | extend, 139 | extendMessages, 140 | addCustomMessages, 141 | niceNames, 142 | koa, 143 | bailable, 144 | collectInputs, 145 | assert, 146 | setStrNotationRepetition: objUtil.setStrNotationRepetition, 147 | empty, 148 | }; 149 | -------------------------------------------------------------------------------- /test/rules/dimensions.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | const fs = require('fs'); 3 | 4 | const { Validator } = require('../../lib/index'); 5 | 6 | describe('dimensions', () => { 7 | it('should pass with exact dimension', async () => { 8 | const v = new Validator( 9 | { file: './test/stubs/file-small.png' }, { file: 'dimensions:minWidth=50,minHeight=32' }, 10 | ); 11 | 12 | const matched = await v.check(); 13 | 14 | assert.equal(matched, true); 15 | }); 16 | 17 | it('should fail with min height', async () => { 18 | const v = new Validator( 19 | { file: './test/stubs/file-small.png' }, { file: 'dimensions:minWidth=50,minHeight=50' }, 20 | ); 21 | 22 | const matched = await v.check(); 23 | 24 | assert.equal(matched, false); 25 | }); 26 | 27 | it('should fail due to min width', async () => { 28 | const v = new Validator( 29 | { file: './test/stubs/file-small.png' }, { file: 'dimensions:minWidth=100' }, 30 | ); 31 | 32 | const matched = await v.check(); 33 | 34 | assert.equal(matched, false); 35 | }); 36 | 37 | it('should fail due to max width', async () => { 38 | const v = new Validator( 39 | { file: './test/stubs/file-small.png' }, { file: 'dimensions:maxWidth=32' }, 40 | ); 41 | 42 | const matched = await v.check(); 43 | 44 | assert.equal(matched, false); 45 | }); 46 | 47 | it('should fail due to max height', async () => { 48 | const v = new Validator( 49 | { file: './test/stubs/file-small.png' }, { file: 'dimensions:maxHeight=30' }, 50 | ); 51 | 52 | const matched = await v.check(); 53 | 54 | assert.equal(matched, false); 55 | }); 56 | 57 | it('should fail due to exact height', async () => { 58 | const v = new Validator( 59 | { file: './test/stubs/file-small.png' }, { file: 'dimensions:width=100' }, 60 | ); 61 | 62 | const matched = await v.check(); 63 | 64 | assert.equal(matched, false); 65 | }); 66 | 67 | it('should pass with excat width,height', async () => { 68 | const v = new Validator( 69 | { file: './test/stubs/file-small.png' }, { file: 'dimensions:width=50,height=32' }, 70 | ); 71 | 72 | const matched = await v.check(); 73 | 74 | assert.equal(matched, true); 75 | }); 76 | 77 | it('should fail with excat width,height', async () => { 78 | const v = new Validator( 79 | { file: { path: './test/stubs/file-small.png' } }, { file: 'dimensions:width=50,height=50' }, 80 | ); 81 | 82 | const matched = await v.check(); 83 | 84 | assert.equal(matched, false); 85 | }); 86 | 87 | it('should pass with buffer', async () => { 88 | const v = new Validator( 89 | { file: fs.readFileSync('./test/stubs/file-small.png') }, { file: 'dimensions:width:50' }, 90 | ); 91 | const matched = await v.check(); 92 | 93 | assert.equal(matched, true); 94 | }); 95 | 96 | it('should pass with nested buffer', async () => { 97 | const v = new Validator( 98 | { file: { buffer: fs.readFileSync('./test/stubs/file-small.png') } }, { file: 'dimensions:width:50' }, 99 | ); 100 | const matched = await v.check(); 101 | 102 | assert.equal(matched, true); 103 | }); 104 | 105 | it('should throw exception', async () => { 106 | try { 107 | const v = new Validator( 108 | { file: ['test'] }, { file: 'dimensions:width:50' }, 109 | ); 110 | await v.check(); 111 | } catch (e) { 112 | assert.equal(e, 'Error: Dimensions rule only accepts Buffer,file path or size property in file object.'); 113 | } 114 | }); 115 | 116 | it('message should exist', async () => { 117 | const v = new Validator( 118 | { file: { path: './test/stubs/file-small.png' } }, { file: 'dimensions:width=50,height=50' }, 119 | ); 120 | 121 | const matched = await v.check(); 122 | 123 | assert.equal(matched, false); 124 | assert.equal( 125 | v.errors.file.message, 126 | v.getExistinParsedMessage({ 127 | rule: 'dimensions', 128 | value: './test/stubs/file-small.png', 129 | attr: 'file', 130 | args: ['width=50', 'height=50'], 131 | }), 132 | ); 133 | }); 134 | }); 135 | -------------------------------------------------------------------------------- /test/objects.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | const { Validator } = require('../lib/index'); 4 | 5 | describe('Objects', () => { 6 | describe('Single Level', () => { 7 | it('should pass with top level required', async () => { 8 | const v = new Validator( 9 | { 10 | product: { 11 | id: '1', name: 'Product', price: '12.50', active: 'yes', 12 | }, 13 | }, 14 | { 15 | product: 'required|object', 16 | 'product.id': 'integer', 17 | 'product.name': 'string', 18 | 'product.price': 'numeric', 19 | 'product.active': 'string', 20 | }, 21 | { 22 | product: 'The given product is invalid :value.', 23 | }, 24 | ); 25 | 26 | const matched = await v.check(); 27 | 28 | assert.equal(matched, true); 29 | }); 30 | 31 | it('should fail with child level required', async () => { 32 | const v = new Validator( 33 | { 34 | product: { 35 | id: 1, name: '', price: '', active: 'yes', 36 | }, 37 | }, 38 | { 39 | product: 'required|object', 40 | 'product.id': 'required|integer', 41 | 'product.name': 'required|string', 42 | 'product.price': 'required|numeric', 43 | 'product.active': 'required|string', 44 | }, 45 | { 46 | product: 'The given product is invalid :value.', 47 | }, 48 | ); 49 | 50 | const matched = await v.check(); 51 | 52 | assert.equal(matched, false); 53 | v.errors.should.have.keys('product.name', 'product.price'); 54 | }); 55 | }); 56 | 57 | describe('Deep Level', () => { 58 | it('should pass with top level required', async () => { 59 | const v = new Validator( 60 | { 61 | product: { 62 | id: '1', 63 | name: 'Product', 64 | price: '12.50', 65 | weight: { unit: 'gram', value: 100 }, 66 | }, 67 | }, 68 | { 69 | product: 'required|object', 70 | 'product.id': 'integer', 71 | 'product.name': 'string', 72 | 'product.price': 'numeric', 73 | 'product.weight': 'object', 74 | 'product.weight.unit': 'in:gram', 75 | 'product.weight.value': 'numeric', 76 | }, 77 | { 78 | product: 'The given product is invalid :value.', 79 | }, 80 | ); 81 | 82 | const matched = await v.check(); 83 | 84 | assert.equal(matched, true); 85 | }); 86 | 87 | it('should fail with child level required', async () => { 88 | const v = new Validator( 89 | { 90 | product: { 91 | id: '1', 92 | name: '', 93 | price: '12.50', 94 | weight: { unit: 'kilo', value: '' }, 95 | }, 96 | }, 97 | { 98 | product: 'required|object', 99 | 'product.id': 'required|integer', 100 | 'product.name': 'required|string', 101 | 'product.price': 'required|numeric', 102 | 'product.weight': 'required|object', 103 | 'product.weight.unit': 'required|in:gram', 104 | 'product.weight.value': 'required|numeric', 105 | }, 106 | { 107 | product: 'The given product is invalid :value.', 108 | }, 109 | ); 110 | 111 | const matched = await v.check(); 112 | 113 | assert.equal(matched, false); 114 | 115 | v.errors.should.have.keys('product.name', 'product.weight.unit', 'product.weight.value'); 116 | }); 117 | 118 | it('should fail with child level missing', async () => { 119 | const v = new Validator( 120 | { 121 | product: { 122 | id: '1', 123 | name: '', 124 | price: '12.50', 125 | }, 126 | }, 127 | { 128 | product: 'required|object', 129 | 'product.id': 'required|integer', 130 | 'product.name': 'required|string', 131 | 'product.price': 'required|numeric', 132 | 'product.weight': 'required|object', 133 | 'product.weight.unit': 'required|in:gram', 134 | 'product.weight.value': 'required|numeric', 135 | }, 136 | { 137 | product: 'The given product is invalid :value.', 138 | }, 139 | ); 140 | 141 | const matched = await v.check(); 142 | 143 | assert.equal(matched, false); 144 | 145 | v.errors.should.have.keys('product.name', 'product.weight', 'product.weight.unit', 'product.weight.value'); 146 | }); 147 | }); 148 | }); 149 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## [4.6.0] 9 | 10 | ### Fixed 11 | - [issue/71](https://github.com/bitnbytesio/node-input-validator/issues/71), [PR](https://github.com/bitnbytesio/node-input-validator/pull/82) 12 | - performance improvement 13 | 14 | ## [4.5.1] 15 | 16 | ### Fixed 17 | - fix vulnerable deps 18 | 19 | ## [4.3.3] 20 | 21 | ### Fixed 22 | 23 | - message ordering 24 | 25 | ### Changed 26 | 27 | - regex rule now accepts flags 28 | 29 | ## [4.3.2] 30 | 31 | ### Fixed 32 | 33 | - url rule breaks with non string value (issues/43) 34 | 35 | ## [4.3.1] 36 | 37 | ### Added 38 | 39 | - support for passing locale to alpha, alphaNumeric, phoneNumber 40 | - wildcardIterations are now configurable 41 | - getErrors() in validator to match v5 api 42 | - validate() in validator to match v5 api 43 | 44 | ### Fixed 45 | 46 | - typescript definition improvements 47 | 48 | ### Changed 49 | 50 | - docs updated 51 | - mime rule improvements 52 | 53 | ### Security 54 | 55 | - deps updated to latest 56 | 57 | ## [4.2.1] 58 | 59 | ### Fixed 60 | 61 | - correct pattren regex in applyOnDeep method to match multi-digit numbers 62 | - issue with addCustomMessages method 63 | 64 | ## [4.2.0-rc2] 65 | 66 | ### Added 67 | 68 | - assert rules to check invalid rules 69 | 70 | ## [4.2.0-rc1] 71 | 72 | ### Added 73 | 74 | - Persian(farsi) support [#24](https://github.com/bitnbytesio/node-input-validator/pull/24) 75 | - rule:different 76 | 77 | ## [4.1.0] 78 | 79 | ### Added 80 | 81 | - support for root level array 82 | 83 | ### Fixed 84 | 85 | - exception if rule againest attr not defined 86 | 87 | ## [4.0.0] 88 | 89 | ### Added 90 | 91 | - multiple errors support 92 | - global level nice names 93 | - support for async and non-async rules 94 | - Deeply nested objects validation 95 | 96 | ### Changed 97 | 98 | - ValidatorInstance.setAttributeNames renamed to ValidatorInstance.niceNames 99 | - Validator.messages renamed to .extendMessages 100 | - Namespace and directory structure 101 | - all previous async rules to sync 102 | - Params of all rules/add new rules/post rules/message parser 103 | 104 | ### Removed 105 | 106 | - Validator.messages in favour of .extendMessages 107 | - Top level Validator class 108 | - Nodejs 7 support dropped 109 | 110 | ### Fixed 111 | 112 | - numeric rule behaviour 113 | 114 | ### Security 115 | 116 | - deps updated to latest 117 | 118 | ## [3.7] 119 | 120 | ### Added 121 | 122 | - dimensions rule 123 | 124 | ### Fixed 125 | 126 | - breakage in case of missing required nested field 127 | 128 | ## [3.6.4] 129 | 130 | ### Fixed 131 | 132 | - breaking changes in message parser due to trimStart for 3.6.* fixed 133 | 134 | ## [3.6.3] 135 | 136 | ### Fixed 137 | 138 | - breaking changes in message parser for 3.6.* fixed 139 | 140 | ## [3.6.2] 141 | 142 | ### Fixed 143 | 144 | - digits rule was not checking for digits 145 | 146 | ## [3.6.1] 147 | 148 | ### Fixed 149 | 150 | - double space in case of camel case attributes 151 | 152 | ## [3.6.0] 153 | 154 | ### Added 155 | 156 | - datetime: attribute must be in format YYYY-MM-DD HH:mm:ss 157 | - dateiso: attribute must be valid iso date 158 | 159 | ### Fixed 160 | 161 | - required: empty check was failing in case of boolean false and int 0 162 | - lengthBetween: no error in case of invalid maximum seed 163 | 164 | ## [3.5.0] 165 | 166 | ### Added 167 | 168 | - arrayUnique: array must contains unique values 169 | - arrayUniqueObjects: array of objects must have unique attribute as per seed 170 | - length: length rule with max and min (optional) seed 171 | 172 | ### Fixed 173 | 174 | - requiredwith, requiredWithout throw exception in case of invalid seed 175 | - integer 0 makes required rule to failed, as 0 was considered as empty 176 | 177 | ## [3.4.2] 178 | 179 | ### Added 180 | 181 | - Example of custom rule using other attributes 182 | 183 | ## [3.4.1] 184 | 185 | ### Fixed 186 | 187 | - crash in case of using non-string rules in constructor will now fails with exception of in valid rule 188 | 189 | ## [3.4.0] 190 | 191 | ### Added 192 | 193 | - gt: greater then another field rule 194 | - gte: greater then or equals another field rule 195 | - lt: less then another field rule 196 | - lte: less then or equals another field rule 197 | 198 | ### Fixed 199 | 200 | - typings 201 | - multiple underscore (_) replacement with space issue 202 | 203 | ## [3.3.0] 204 | 205 | ### Fixed 206 | 207 | - between rule 208 | - lengthBetween rule 209 | 210 | ### Security 211 | 212 | - npm audit vulnerabilities fix 213 | -------------------------------------------------------------------------------- /test/rules/between.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | const { Validator } = require('../../lib/index'); 4 | 5 | describe('between', () => { 6 | it('should pass with integer', async () => { 7 | const v = new Validator( 8 | { age: '19' }, 9 | { age: 'between:16,21' }, 10 | ); 11 | 12 | const matched = await v.check(); 13 | 14 | assert.equal(matched, true); 15 | }); 16 | 17 | it('should fail with max seed', async () => { 18 | const v = new Validator( 19 | { age: '29' }, 20 | { age: 'between:16,21' }, 21 | ); 22 | 23 | const matched = await v.check(); 24 | 25 | assert.equal(matched, false); 26 | }); 27 | 28 | it('should fail with min seed', async () => { 29 | const v = new Validator( 30 | { age: '15' }, 31 | { age: 'between:16,21' }, 32 | ); 33 | 34 | const matched = await v.check(); 35 | 36 | assert.equal(matched, false); 37 | }); 38 | 39 | it('should pass with decimal input and integer seeds', async () => { 40 | const v = new Validator( 41 | { price: '19.99' }, 42 | { price: 'between:19,20' }, 43 | ); 44 | 45 | const matched = await v.check(); 46 | 47 | assert.equal(matched, true); 48 | }); 49 | 50 | it('should pass with decimal input and mixed seeds', async () => { 51 | const v = new Validator( 52 | { price: '19.98' }, 53 | { price: 'between:19,19.98' }, 54 | ); 55 | 56 | const matched = await v.check(); 57 | 58 | assert.equal(matched, true); 59 | }); 60 | 61 | it('should fail with max decimal seed', async () => { 62 | const v = new Validator( 63 | { price: '19.99' }, 64 | { price: 'between:19,19.89' }, 65 | ); 66 | 67 | const matched = await v.check(); 68 | 69 | assert.equal(matched, false); 70 | }); 71 | 72 | it('should pass with array length', async () => { 73 | const v = new Validator( 74 | { features: [1, 2, 3] }, 75 | { features: 'between:3,5' }, 76 | ); 77 | 78 | const matched = await v.check(); 79 | 80 | assert.equal(matched, true); 81 | }); 82 | 83 | it('should fail with array min length', async () => { 84 | const v = new Validator( 85 | { features: [1, 2] }, 86 | { features: 'between:3,6' }, 87 | ); 88 | 89 | const matched = await v.check(); 90 | 91 | assert.equal(matched, false); 92 | }); 93 | 94 | it('should fail with array max length', async () => { 95 | const v = new Validator( 96 | { features: [1, 2, 3, 4, 5, 6] }, 97 | { features: 'between:3,5' }, 98 | ); 99 | 100 | const matched = await v.check(); 101 | 102 | assert.equal(matched, false); 103 | }); 104 | 105 | it('should throw invalid seed count exception', async () => { 106 | try { 107 | const v = new Validator({ attribute: 'Harcharan Singh' }, { attribute: 'required|between:a' }); 108 | 109 | await v.check(); 110 | 111 | throw new Error('Invalid seed exception.'); 112 | } catch (e) { 113 | assert.equal(e, 'Error: The number of arguments for between in the field attribute are invalid.'); 114 | } 115 | }); 116 | 117 | it('should throw invalid min seed exception', async () => { 118 | try { 119 | const v = new Validator({ attribute: 'Harcharan Singh' }, { attribute: 'required|between:a,10' }); 120 | 121 | await v.check(); 122 | 123 | throw new Error('Invalid seed exception.'); 124 | } catch (e) { 125 | assert.equal(e, 'Error: Seeds must be numeric for attribute under between rule.'); 126 | } 127 | }); 128 | 129 | it('should throw invalid max seed exception', async () => { 130 | try { 131 | const v = new Validator({ attribute: 'Harcharan Singh' }, { attribute: 'required|between:10,b' }); 132 | 133 | await v.check(); 134 | 135 | throw new Error('Invalid seed exception.'); 136 | } catch (e) { 137 | assert.equal(e, 'Error: Seeds must be numeric for attribute under between rule.'); 138 | } 139 | }); 140 | 141 | it('should throw min must be less then max seed exception', async () => { 142 | try { 143 | const v = new Validator({ attribute: 'Harcharan Singh' }, { attribute: 'required|between:10,5' }); 144 | 145 | await v.check(); 146 | 147 | throw new Error('Invalid seed exception.'); 148 | } catch (e) { 149 | assert.equal(e, 'Error: Seed min must be less then max in between rule for attribute.'); 150 | } 151 | }); 152 | 153 | it('message should exist', async () => { 154 | const v = new Validator( 155 | { attrs: 'abc' }, 156 | { attrs: 'between:1,5' }, 157 | ); 158 | 159 | const matched = await v.check(); 160 | 161 | assert.equal(matched, false); 162 | assert.equal( 163 | v.errors.attrs.message, 164 | v.getExistinParsedMessage({ 165 | rule: 'between', 166 | value: {}, 167 | attr: 'attrs', 168 | args: [1, 5], 169 | }), 170 | ); 171 | }); 172 | }); 173 | --------------------------------------------------------------------------------