├── .eslintrc ├── .gitignore ├── .travis.yml ├── Makefile ├── README.md ├── example.js ├── index.js ├── package.json └── test ├── each.js └── test.js /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "eslint:recommended", 3 | "env": { 4 | "node": true 5 | }, 6 | "rules": { 7 | "camelcase": 2, 8 | "semi": 2 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.sw* 3 | 4 | # Coverage report directory 5 | coverage 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "0.10" 4 | - "0.12" 5 | - "iojs" 6 | after_success: 7 | - make coveralls 8 | sudo: false 9 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | ISTANBUL=node_modules/.bin/istanbul 3 | TAPE=node_modules/.bin/tape 4 | COVERALLS=node_modules/.bin/coveralls 5 | 6 | test: 7 | @$(TAPE) test/*.js 8 | 9 | cover: 10 | @$(ISTANBUL) cover $(TAPE) -- test/*.js 11 | 12 | coveralls: 13 | @$(ISTANBUL) cover --report lcovonly $(TAPE) -- test/*.js && cat ./coverage/lcov.info | $(COVERALLS) 14 | 15 | 16 | .PHONY: test cover coveralls 17 | 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # validimir 3 | 4 | Minimalistic validation functions with a fluid api, to be used with flexible model layers or as standalone. 5 | 6 | [![build status](https://secure.travis-ci.org/juliangruber/validimir.png)](http://travis-ci.org/juliangruber/validimir) 7 | [![Dependency Status](https://david-dm.org/juliangruber/validimir.svg)](https://david-dm.org/juliangruber/validimir) 8 | [![Coverage Status](https://coveralls.io/repos/juliangruber/validimir/badge.svg?branch=master&service=github)](https://coveralls.io/github/juliangruber/validimir?branch=master) 9 | 10 | ## Example 11 | 12 | ```js 13 | var v = require('validimir'); 14 | 15 | var fn = v().object().hasKey('foo').each(v().string()); 16 | 17 | fn({ foo: 'bar', beep: 'boop' }); 18 | // => { errors: [] } 19 | 20 | fn({ foo: 'bar', beep: 2 }); 21 | // => { errors: [{ 22 | // value: 2, 23 | // operator: 'string', 24 | // actual: 'number', 25 | // message: 'Expected a string but got a number' 26 | // }] } 27 | 28 | fn({ beep: 2 }); 29 | // => { errors: [ 30 | // { 31 | // value: 2, 32 | // operator: 'string', 33 | // actual: 'number', 34 | // message: 'Expected a string but got a number' 35 | // }, 36 | // { 37 | // value: { beep: 2 }, 38 | // operator: 'hasKey', 39 | // excepted: 'foo', 40 | // message: 'Expected {"beep":2} to have key foo' 41 | // } 42 | // ]} 43 | ``` 44 | 45 | ## Installation 46 | 47 | With [npm](https://npmjs.org) do: 48 | 49 | ```bash 50 | npm install validimir 51 | ``` 52 | 53 | ## API 54 | 55 | Validimir will provide you with a useable `.message` for errors, or you can pass in your own to each method. 56 | 57 | ### v() 58 | 59 | Create a new validation function. 60 | 61 | ### .number([message]) 62 | ### .string([message]) 63 | ### .boolean([message]) 64 | ### .object([message]) 65 | ### .array([message]) 66 | ### .buffer([message]) 67 | ### .date([message]) 68 | 69 | Assert value is of given type. Types are exact, so `.array()` won't accept an object and vice versa. 70 | 71 | ### .email([message]) 72 | 73 | Assert value is a valid email. The regular expression used is: 74 | 75 | ``` 76 | /^([\w_\.\-\+])+\@([\w\-]+\.)+([\w]{2,10})+$/ 77 | ``` 78 | 79 | ### .equal(value[, message]) 80 | ### .notEqual(value[, message]) 81 | 82 | Assert value is (or not) equal to `value`. [ltgt](http://npmjs.org/package/ltgt) ranges can be used as well. 83 | 84 | ### .match(reg[, message]) 85 | ### .notMatch(reg[, message]) 86 | 87 | Assert value matches (or doesn't match) regular expression `reg`. 88 | 89 | ### .hasKey(key[, message]) 90 | 91 | Assert object value has key `key`. 92 | 93 | ### .len(length[, message]) 94 | 95 | Assert value is of length `length`. [ltgt](http://npmjs.org/package/ltgt) ranges can be used as well. 96 | 97 | ### .of(array[, message]) 98 | ### .notOf(array[, message]) 99 | 100 | Assert value can (or can't) be found in `array`. 101 | 102 | ### .each(fn) 103 | 104 | Assert each of value - no matter whether it's an array or object - passes `fn` with should be a function returned by validimir or an api compatible module. 105 | 106 | ### .custom(fn) 107 | 108 | Add a custom check `fn`. 109 | 110 | A function is expected to take a value, validate it synchronously and returns either `undefined` on success or a truthy error value on failure. In theory error values can be of any truthy type, but to be consistent with other checks in validimir it should be an object with the following properties: 111 | 112 | - `value`: The value that didn't pass the validation 113 | - `operator`: The name of the operator that failed 114 | - `message`: A descriptive error messsage 115 | - `expected`: The expected value, if any 116 | 117 | Example: 118 | 119 | ```js 120 | var v = require('validimir'); 121 | var isIP = require('validator').isIP; 122 | var checkIP = function(value) { 123 | if (!isIP(value)) { 124 | return { 125 | value: value, 126 | operator: 'ip', 127 | message: 'Expected a valid IP address' 128 | } 129 | } 130 | }; 131 | var fn = v().custom(checkIP); 132 | 133 | fn('not an ip address').errors; 134 | // => [ { value: 'not an ip address', 135 | // operator: 'ip', 136 | // message: 'Expected a valid IP address' } ] 137 | 138 | fn('127.0.0.1').errors; 139 | // => [] 140 | ``` 141 | 142 | ### .errors 143 | 144 | Array of errors objects found validating value. Accessible on the result of calling the validation function, e.g. 145 | 146 | ```js 147 | var v = require('validimir'); 148 | v().number()(13).errors; 149 | ``` 150 | 151 | ### .valid() 152 | 153 | Helper function asserting `.errors.length === 0`. Accessible on the result of calling the validation function, e.g. 154 | 155 | ```js 156 | var v = require('validimir'); 157 | v().string()('13').valid(); 158 | ``` 159 | 160 | ## License 161 | 162 | (MIT) 163 | 164 | Copyright (c) 2013 Julian Gruber <julian@juliangruber.com> 165 | 166 | Permission is hereby granted, free of charge, to any person obtaining a copy of 167 | this software and associated documentation files (the "Software"), to deal in 168 | the Software without restriction, including without limitation the rights to 169 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 170 | of the Software, and to permit persons to whom the Software is furnished to do 171 | so, subject to the following conditions: 172 | 173 | The above copyright notice and this permission notice shall be included in all 174 | copies or substantial portions of the Software. 175 | 176 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 177 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 178 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 179 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 180 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 181 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 182 | SOFTWARE. 183 | -------------------------------------------------------------------------------- /example.js: -------------------------------------------------------------------------------- 1 | var v = require('./'); 2 | 3 | log(v().number()(13)); 4 | log(v().number()('foo')); 5 | 6 | log(v().string()('string')); 7 | log(v().string()(13)); 8 | 9 | log(v().boolean()(true)); 10 | log(v().boolean()('string')); 11 | 12 | log(v().object()({})); 13 | log(v().object()([])); 14 | 15 | log(v().array()([])); 16 | log(v().array()({})); 17 | 18 | log(v().buffer()(new Buffer(0))); 19 | log(v().buffer()({})); 20 | 21 | log(v().equal('foo')('foo')); 22 | log(v().equal('foo')('bar')); 23 | 24 | log(v().notEqual('foo')('bar')); 25 | log(v().notEqual('foo')('foo')); 26 | 27 | log(v().match(/foo/)('foo')); 28 | log(v().match(/foo/)('bar')); 29 | 30 | log(v().notMatch(/foo/)('bar')); 31 | log(v().notMatch(/foo/)('foo')); 32 | 33 | log(v().hasKey('foo')({ foo: 'bar' })); 34 | log(v().hasKey('foo')({ beep: 'boop' })); 35 | 36 | log(v().object().hasKey('foo')({ foo: 'bar' })); 37 | log(v().object().hasKey('foo')(13)); 38 | 39 | log(v().len(13)('aaaaaaaaaaaaa')); 40 | log(v().len(13)('aaaaaaaaaaaa')); 41 | 42 | log(v().of(['foo', 'bar'])('foo')); 43 | log(v().of(['foo', 'bar'])('baz')); 44 | 45 | log(v().notOf(['foo', 'bar'])('baz')); 46 | log(v().notOf(['foo', 'bar'])('foo')); 47 | 48 | log(v().len({ gt: 3 })('aaaaaaaaaaaaa')); 49 | log(v().len({ gt: 3 })('a')); 50 | 51 | log(v().equal({ gt: 3 })(4)); 52 | log(v().equal({ gt: 3 })(3)); 53 | 54 | log(v().notEqual({ gt: 3 })(3)); 55 | log(v().notEqual({ gt: 3 })(4)); 56 | 57 | log(v().each(v().string())(['foo', 'bar'])); 58 | log(v().each(v().string())(['foo', 13])); 59 | 60 | function log(val){ 61 | console.log(val.errors); 62 | } 63 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var type = require('component-type'); 2 | var ltgt = require('ltgt'); 3 | var fmt = require('util').format; 4 | var toInterval = require('ltgt-to-interval'); 5 | 6 | module.exports = function V(){ 7 | var checks = []; 8 | var v = function(value){ 9 | var errors = []; 10 | checks.forEach(function(check){ 11 | var errs = check(value); 12 | if (!errs) return; 13 | if (!Array.isArray(errs)) errs = [errs]; 14 | errs.forEach(function(err){ errors.push(err) }); 15 | }); 16 | return { 17 | errors: errors, 18 | valid: function() { 19 | return errors.length == 0; 20 | } 21 | }; 22 | }; 23 | var addCheck = function(op, fn){ 24 | v[op] = function(){ 25 | var check = fn.apply(null, arguments); 26 | checks.push(check); 27 | return v; 28 | }; 29 | }; 30 | 31 | 32 | addCheck('custom', function(fn) { 33 | return fn; 34 | }); 35 | 36 | var types = 'number string boolean object array buffer date'.split(' '); 37 | types.forEach(function(t){ 38 | addCheck(t, function(msg){ 39 | return function(v){ 40 | if (type(v) != t) return { 41 | value: v, 42 | operator: t, 43 | actual: type(v), 44 | message: msg || fmt('Expected a %s but got a %s', t, type(v)) 45 | }; 46 | }; 47 | }); 48 | }); 49 | 50 | addCheck('email', function(msg){ 51 | return function(e){ 52 | var match = type(e) == 'string' && /^([\w_\.\-\+])+\@([\w\-]+\.)+([\w]{2,10})+$/.test(e); 53 | if (!match) return { 54 | value: e, 55 | operator: 'email', 56 | message: msg || 'Expected a valid email address' 57 | }; 58 | }; 59 | }); 60 | 61 | addCheck('equal', function(expected, msg){ 62 | if (isObj(expected)) { 63 | return function(v){ 64 | if (!ltgt.contains(expected, v)) return { 65 | value: v, 66 | operator: 'equal', 67 | expected: expected, 68 | message: msg || fmt('Expected a value in range %s', toInterval(expected)) 69 | }; 70 | }; 71 | } else { 72 | return function(v){ 73 | if (v !== expected) return { 74 | value: v, 75 | operator: 'equal', 76 | expected: expected, 77 | message: msg || fmt('Expected %j to equal %j', v, expected) 78 | }; 79 | }; 80 | } 81 | 82 | }); 83 | 84 | addCheck('notEqual', function(notExpected, msg){ 85 | if (isObj(notExpected)) { 86 | return function(v){ 87 | if (ltgt.contains(notExpected, v)) return { 88 | value: v, 89 | operator: 'notEqual', 90 | message: msg 91 | || fmt('Expected a value outside range %s', toInterval(notExpected)) 92 | }; 93 | }; 94 | } else { 95 | return function(v){ 96 | if (v === notExpected) return { 97 | value: v, 98 | operator: 'notEqual', 99 | message: msg || fmt('Expected %j not to equal %j', v, notExpected) 100 | }; 101 | }; 102 | } 103 | }); 104 | 105 | addCheck('match', function(reg, msg){ 106 | return function(v){ 107 | if (type(v) != 'string' || !reg.test(v)) return { 108 | value: v, 109 | operator: 'match', 110 | expected: reg, 111 | message: msg || fmt('Expected %j to match %s', v, reg) 112 | }; 113 | }; 114 | }); 115 | 116 | addCheck('notMatch', function(reg, msg){ 117 | return function(v){ 118 | if (type(v) != 'string' || reg.test(v)) return { 119 | value: v, 120 | operator: 'notMatch', 121 | message: msg || fmt('Expected %j not to match %s', v, reg) 122 | }; 123 | }; 124 | }); 125 | 126 | addCheck('hasKey', function(k, msg){ 127 | return function(o){ 128 | if (type(o) != 'object' || !(k in o)) return { 129 | value: o, 130 | operator: 'hasKey', 131 | expected: k, 132 | message: msg || fmt('Expected %j to have key %s', o, k) 133 | }; 134 | }; 135 | }); 136 | 137 | addCheck('len', function(l, msg){ 138 | if (typeof l == 'number') { 139 | return function(s){ 140 | if (getLength(s) != l) return { 141 | value: s, 142 | operator: 'len', 143 | expected: l, 144 | actual: getLength(s), 145 | message: msg || fmt('Expected %j to have length %s', s, l) 146 | }; 147 | }; 148 | } else { 149 | return function(s){ 150 | if (!ltgt.contains(l, getLength(s))) return { 151 | value: s, 152 | operator: 'len', 153 | expected: l, 154 | actual: getLength(s), 155 | message: msg 156 | || fmt('Expected %j to be of length %s', s, toInterval(l)) 157 | }; 158 | }; 159 | } 160 | }); 161 | 162 | addCheck('of', function(arr, msg){ 163 | return function(v){ 164 | if (arr.indexOf(v) == -1) return { 165 | value: v, 166 | operator: 'of', 167 | expected: arr, 168 | message: msg || fmt('Expected %j to be of %j', v, arr) 169 | }; 170 | }; 171 | }); 172 | 173 | addCheck('notOf', function(arr, msg){ 174 | return function(v){ 175 | if (arr.indexOf(v) > -1) return { 176 | value: v, 177 | operator: 'notOf', 178 | expected: arr, 179 | message: msg || fmt('Expected %j not to be of %j', v, arr) 180 | }; 181 | }; 182 | }); 183 | 184 | addCheck('each', function(fn){ 185 | return function(o){ 186 | var errors = []; 187 | Object.keys(o).forEach(function(key){ 188 | var errs = fn(o[key]).errors; 189 | errs.forEach(function(err) { errors.push(err) }); 190 | }); 191 | if (errors.length) return errors; 192 | }; 193 | }); 194 | 195 | return v; 196 | }; 197 | 198 | module.exports.putin = function(key, value, cb) { 199 | cb(new Error('in node, error throws you!')); 200 | }; 201 | 202 | function getLength(obj){ 203 | return obj != null && typeof obj.length != 'undefined' 204 | ? obj.length 205 | : undefined; 206 | } 207 | 208 | 209 | function isObj(obj) { 210 | return Object(obj) === obj; 211 | } 212 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "validimir", 3 | "description": "Create validation functions.", 4 | "version": "1.5.0", 5 | "repository": { 6 | "type": "git", 7 | "url": "git://github.com/juliangruber/validimir.git" 8 | }, 9 | "homepage": "https://github.com/juliangruber/validimir", 10 | "main": "index.js", 11 | "scripts": { 12 | "test": "make test" 13 | }, 14 | "dependencies": { 15 | "component-type": "1.2.1", 16 | "ltgt": "2.1.3", 17 | "ltgt-to-interval": "^1.0.0", 18 | "quote-unquote": "1.0.0" 19 | }, 20 | "devDependencies": { 21 | "coveralls": "^2.11.11", 22 | "istanbul": "^0.4.4", 23 | "tape": "^4.6.0" 24 | }, 25 | "keywords": [ 26 | "curry", 27 | "validate", 28 | "validation" 29 | ], 30 | "author": { 31 | "name": "Julian Gruber", 32 | "email": "mail@juliangruber.com", 33 | "url": "http://juliangruber.com" 34 | }, 35 | "license": "MIT", 36 | "testling": { 37 | "files": "test/*.js", 38 | "browsers": [ 39 | "ie/8..latest", 40 | "firefox/20..latest", 41 | "firefox/nightly", 42 | "chrome/25..latest", 43 | "chrome/canary", 44 | "opera/12..latest", 45 | "opera/next", 46 | "safari/5.1..latest", 47 | "ipad/6.0..latest", 48 | "iphone/6.0..latest", 49 | "android-browser/4.2..latest" 50 | ] 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /test/each.js: -------------------------------------------------------------------------------- 1 | var v = require('..'); 2 | var test = require('tape'); 3 | 4 | test('each', function(t) { 5 | var validate = v().each(v().string()); 6 | 7 | t.deepEqual(validate(['foo', 'bar']).errors, []); 8 | t.deepEqual(validate({ foo: 'foo', bar: 'bar' }).errors, []); 9 | t.deepEqual(validate(['foo', 13]).errors, [ 10 | { 11 | value: 13, 12 | operator: 'string', 13 | actual: 'number', 14 | message: 'Expected a string but got a number' 15 | } 16 | ]); 17 | t.deepEqual(validate({ foo: 13 }).errors, [ 18 | { 19 | value: 13, 20 | operator: 'string', 21 | actual: 'number', 22 | message: 'Expected a string but got a number' 23 | } 24 | ]); 25 | 26 | t.end(); 27 | }); 28 | 29 | -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | var test = require('tape'); 2 | var v = require('..'); 3 | 4 | test('number', function(t) { 5 | t.deepEqual(v().number()(13).errors, []); 6 | t.equal(v().number()(13).valid(), true); 7 | t.deepEqual(v().number()('13').errors, [ 8 | { 9 | value: '13', 10 | operator: 'number', 11 | actual: 'string', 12 | message: 'Expected a number but got a string' 13 | } 14 | ]); 15 | t.deepEqual(v().number('number')('13').errors, [ 16 | { 17 | value: '13', 18 | operator: 'number', 19 | actual: 'string', 20 | message: 'number' 21 | } 22 | ]); 23 | t.equal(v().number()('13').valid(), false); 24 | t.end(); 25 | }); 26 | 27 | test('string', function(t) { 28 | t.deepEqual(v().string()('13').errors, []); 29 | t.deepEqual(v().string()(13).errors, [ 30 | { 31 | value: 13, 32 | operator: 'string', 33 | actual: 'number', 34 | message: 'Expected a string but got a number' 35 | } 36 | ]); 37 | t.deepEqual(v().string('string')(13).errors, [ 38 | { 39 | value: 13, 40 | operator: 'string', 41 | actual: 'number', 42 | message: 'string' 43 | } 44 | ]); 45 | t.end(); 46 | }); 47 | 48 | test('boolean', function(t) { 49 | t.deepEqual(v().boolean()(true).errors, []); 50 | t.deepEqual(v().boolean()('true').errors, [ 51 | { 52 | value: 'true', 53 | operator: 'boolean', 54 | actual: 'string', 55 | message: 'Expected a boolean but got a string' 56 | } 57 | ]); 58 | t.deepEqual(v().boolean('boolean')('true').errors, [ 59 | { 60 | value: 'true', 61 | operator: 'boolean', 62 | actual: 'string', 63 | message: 'boolean' 64 | } 65 | ]); 66 | t.end(); 67 | }); 68 | 69 | test('object', function(t) { 70 | t.deepEqual(v().object()({}).errors, []); 71 | t.deepEqual(v().object()('true').errors, [ 72 | { 73 | value: 'true', 74 | operator: 'object', 75 | actual: 'string', 76 | message: 'Expected a object but got a string' 77 | } 78 | ]); 79 | t.deepEqual(v().object('object')('true').errors, [ 80 | { 81 | value: 'true', 82 | operator: 'object', 83 | actual: 'string', 84 | message: 'object' 85 | } 86 | ]); 87 | t.end(); 88 | }); 89 | 90 | test('array', function(t) { 91 | t.deepEqual(v().array()([]).errors, []); 92 | t.deepEqual(v().array()('true').errors, [ 93 | { 94 | value: 'true', 95 | operator: 'array', 96 | actual: 'string', 97 | message: 'Expected a array but got a string' 98 | } 99 | ]); 100 | t.deepEqual(v().array('array')('true').errors, [ 101 | { 102 | value: 'true', 103 | operator: 'array', 104 | actual: 'string', 105 | message: 'array' 106 | } 107 | ]); 108 | t.end(); 109 | }); 110 | 111 | test('buffer', function(t) { 112 | t.deepEqual(v().buffer()(new Buffer(0)).errors, []); 113 | t.deepEqual(v().buffer()({}).errors, [ 114 | { 115 | value: {}, 116 | operator: 'buffer', 117 | actual: 'object', 118 | message: 'Expected a buffer but got a object' 119 | } 120 | ]); 121 | t.deepEqual(v().buffer('buffer')({}).errors, [ 122 | { 123 | value: {}, 124 | operator: 'buffer', 125 | actual: 'object', 126 | message: 'buffer' 127 | } 128 | ]); 129 | t.end(); 130 | }); 131 | 132 | test('date', function(t) { 133 | t.deepEqual(v().date()(new Date).errors, []); 134 | t.deepEqual(v().date()({}).errors, [ 135 | { 136 | value: {}, 137 | operator: 'date', 138 | actual: 'object', 139 | message: 'Expected a date but got a object' 140 | } 141 | ]); 142 | t.deepEqual(v().date('date')({}).errors, [ 143 | { 144 | value: {}, 145 | operator: 'date', 146 | actual: 'object', 147 | message: 'date' 148 | } 149 | ]); 150 | t.end(); 151 | }); 152 | 153 | test('email', function(t) { 154 | t.deepEqual(v().email()('foo@bar.com').errors, []); 155 | t.deepEqual(v().email()('foo@bar').errors, [ 156 | { 157 | value: 'foo@bar', 158 | operator: 'email', 159 | message: 'Expected a valid email address' 160 | } 161 | ]); 162 | t.deepEqual(v().email('email')('foo@bar').errors, [ 163 | { 164 | value: 'foo@bar', 165 | operator: 'email', 166 | message: 'email' 167 | } 168 | ]); 169 | t.deepEqual(v().email('email')(null).errors, [ 170 | { 171 | value: null, 172 | operator: 'email', 173 | message: 'email' 174 | } 175 | ]); 176 | t.end(); 177 | }); 178 | 179 | test('equal', function(t) { 180 | t.deepEqual(v().equal('foo')('foo').errors, []); 181 | t.deepEqual(v().equal(4)(4).errors, []); 182 | 183 | t.deepEqual(v().equal({ gt: 4 })(6).errors, []); 184 | t.deepEqual(v().equal({ gt: 'a' })('b').errors, []); 185 | 186 | t.deepEqual(v().equal({ lt: 7 })(6).errors, []); 187 | t.deepEqual(v().equal({ lt: 'b' })('a').errors, []); 188 | 189 | t.deepEqual(v().equal(1)(2).errors, [ 190 | { 191 | value: 2, 192 | operator: 'equal', 193 | expected: 1, 194 | message: 'Expected 2 to equal 1' 195 | } 196 | ]); 197 | t.deepEqual(v().equal(1, 'equal')(2).errors, [ 198 | { 199 | value: 2, 200 | operator: 'equal', 201 | expected: 1, 202 | message: 'equal' 203 | } 204 | ]); 205 | 206 | 207 | t.deepEquals(v().equal('1')(1).errors, [ 208 | { 209 | value: 1, 210 | operator: 'equal', 211 | expected: '1', 212 | message: 'Expected 1 to equal "1"' 213 | } 214 | ]); 215 | t.deepEquals(v().equal({ gt: 4 })(3).errors, [ 216 | { 217 | value: 3, 218 | operator: 'equal', 219 | expected: { gt: 4 }, 220 | message: 'Expected a value in range (4,' 221 | } 222 | ]); 223 | t.deepEquals(v().equal({ gt: 4 }, 'equal')(3).errors, [ 224 | { 225 | value: 3, 226 | operator: 'equal', 227 | expected: { gt: 4 }, 228 | message: 'equal' 229 | } 230 | ]); 231 | t.deepEqual(v().equal({ gt: 'b' })('a').errors, [ 232 | { 233 | value: 'a', 234 | operator: 'equal', 235 | expected: { gt: 'b' }, 236 | message: 'Expected a value in range (b,' 237 | } 238 | ]); 239 | t.deepEqual(v().equal(null)('a').errors, [ 240 | { 241 | value: 'a', 242 | operator: 'equal', 243 | expected: null, 244 | message: 'Expected "a" to equal null' 245 | } 246 | ]); 247 | t.deepEqual(v().equal(null)(null).errors, []); 248 | t.end(); 249 | }); 250 | 251 | test('notEqual', function(t) { 252 | t.deepEqual(v().notEqual('1')(1).errors, []); 253 | t.deepEqual(v().notEqual({ gt: 3, lt: 5 })(6).errors, []); 254 | t.deepEqual(v().notEqual('1')('1').errors, [ 255 | { 256 | value: '1', 257 | operator: 'notEqual', 258 | message: 'Expected "1" not to equal "1"' 259 | } 260 | ]); 261 | t.deepEqual(v().notEqual('1', 'not equal')('1').errors, [ 262 | { 263 | value: '1', 264 | operator: 'notEqual', 265 | message: 'not equal' 266 | } 267 | ]); 268 | t.deepEqual(v().notEqual({ gt: 3, lt: 5 })(4).errors, [ 269 | { 270 | value: 4, 271 | operator: 'notEqual', 272 | message: 'Expected a value outside range (3,5)' 273 | } 274 | ]); 275 | t.deepEqual(v().notEqual({ gt: 3, lt: 5 }, 'not equal')(4).errors, [ 276 | { 277 | value: 4, 278 | operator: 'notEqual', 279 | message: 'not equal' 280 | } 281 | ]); 282 | t.deepEqual(v().notEqual(null)(null).errors, [ 283 | { 284 | value: null, 285 | operator: 'notEqual', 286 | message: 'Expected null not to equal null' 287 | } 288 | ]); 289 | t.deepEqual(v().notEqual(null)('a').errors, []); 290 | t.end(); 291 | }); 292 | 293 | test('match', function(t) { 294 | t.deepEqual(v().match(/foo/)('foo').errors, []); 295 | t.deepEqual(v().match(/foo/)('f').errors, [ 296 | { 297 | value: 'f', 298 | operator: 'match', 299 | expected: /foo/, 300 | message: 'Expected "f" to match /foo/' 301 | } 302 | ]); 303 | t.deepEqual(v().match(/foo/, 'match')('f').errors, [ 304 | { 305 | value: 'f', 306 | operator: 'match', 307 | expected: /foo/, 308 | message: 'match' 309 | } 310 | ]); 311 | t.deepEqual(v().match(/foo/)(null).errors, [ 312 | { 313 | value: null, 314 | operator: 'match', 315 | expected: /foo/, 316 | message: 'Expected null to match /foo/' 317 | } 318 | ]); 319 | t.end(); 320 | }); 321 | 322 | test('notMatch', function(t) { 323 | t.deepEqual(v().notMatch(/foo/)('f').errors, []); 324 | t.deepEqual(v().notMatch(/foo/)('foo').errors, [ 325 | { 326 | value: 'foo', 327 | operator: 'notMatch', 328 | message: 'Expected "foo" not to match /foo/' 329 | } 330 | ]); 331 | t.deepEqual(v().notMatch(/foo/)(null).errors, [ 332 | { 333 | value: null, 334 | operator: 'notMatch', 335 | message: 'Expected null not to match /foo/' 336 | } 337 | ]); 338 | t.deepEqual(v().notMatch(/foo/, 'not match')('foo').errors, [ 339 | { 340 | value: 'foo', 341 | operator: 'notMatch', 342 | message: 'not match' 343 | } 344 | ]); 345 | t.end(); 346 | }); 347 | 348 | test('hasKey', function(t) { 349 | t.deepEqual(v().hasKey('a')({a:'b'}).errors, []); 350 | t.deepEqual(v().hasKey('b')({a:'b'}).errors, [ 351 | { 352 | value: { a: 'b' }, 353 | operator: 'hasKey', 354 | expected: 'b', 355 | message: 'Expected {"a":"b"} to have key b' 356 | } 357 | ]); 358 | t.deepEqual(v().hasKey('b', 'has key')({a:'b'}).errors, [ 359 | { 360 | value: { a: 'b' }, 361 | operator: 'hasKey', 362 | expected: 'b', 363 | message: 'has key' 364 | } 365 | ]); 366 | t.deepEqual(v().hasKey('b')(null).errors, [ 367 | { 368 | value: null, 369 | operator: 'hasKey', 370 | expected: 'b', 371 | message: 'Expected null to have key b' 372 | } 373 | ]); 374 | t.end(); 375 | }); 376 | 377 | test('len', function(t) { 378 | t.deepEqual(v().len(13)('aaaaaaaaaaaaa').errors, []); 379 | t.deepEqual(v().len({ gt: 3 })('aaaaa').errors, []); 380 | t.deepEqual(v().len({ lte: 10 })('aaaaa').errors, []); 381 | t.deepEqual(v().len({ gt: 3, lte: 10 })('aaaaa').errors, []); 382 | 383 | t.deepEqual(v().len(13)('').errors, [ 384 | { 385 | value: '', 386 | operator: 'len', 387 | expected: 13, 388 | actual: 0, 389 | message: 'Expected "" to have length 13' 390 | } 391 | ]); 392 | t.deepEqual(v().len(13, 'len')('a').errors, [ 393 | { 394 | value: 'a', 395 | operator: 'len', 396 | expected: 13, 397 | actual: 1, 398 | message: 'len' 399 | } 400 | ]); 401 | t.deepEqual(v().len({ gt: 3 })('a').errors, [ 402 | { 403 | value: 'a', 404 | operator: 'len', 405 | expected: { gt: 3 }, 406 | actual: 1, 407 | message: 'Expected "a" to be of length (3,' 408 | } 409 | ]); 410 | t.deepEqual(v().len({ gt: 3 }, 'len')('a').errors, [ 411 | { 412 | value: 'a', 413 | operator: 'len', 414 | expected: { gt: 3 }, 415 | actual: 1, 416 | message: 'len' 417 | } 418 | ]); 419 | t.deepEqual(v().len({ lte: 3 })('aaaaa').errors, [ 420 | { 421 | value: 'aaaaa', 422 | operator: 'len', 423 | expected: { lte: 3 }, 424 | actual: 5, 425 | message: 'Expected "aaaaa" to be of length ,3]' 426 | } 427 | ]); 428 | t.deepEqual(v().len({ gt: 3, lte: 10 })('a').errors, [ 429 | { 430 | value: 'a', 431 | operator: 'len', 432 | expected: { gt: 3, lte: 10 }, 433 | actual: 1, 434 | message: 'Expected "a" to be of length (3,10]' 435 | } 436 | ]); 437 | t.deepEqual(v().len(3)(null).errors, [ 438 | { 439 | value: null, 440 | operator: 'len', 441 | expected: 3, 442 | actual: undefined, 443 | message: 'Expected null to have length 3' 444 | } 445 | ]); 446 | t.deepEqual(v().len(3)(undefined).errors, [ 447 | { 448 | value: undefined, 449 | operator: 'len', 450 | expected: 3, 451 | actual: undefined, 452 | message: 'Expected undefined to have length 3' 453 | } 454 | ]); 455 | t.deepEqual(v().len({ gt:3 })(null).errors, [ 456 | { 457 | value: null, 458 | operator: 'len', 459 | expected: { gt: 3 }, 460 | actual: undefined, 461 | message: 'Expected null to be of length (3,' 462 | } 463 | ]); 464 | t.deepEqual(v().len({ gt:3 })(undefined).errors, [ 465 | { 466 | value: undefined, 467 | operator: 'len', 468 | expected: { gt: 3 }, 469 | actual: undefined, 470 | message: 'Expected undefined to be of length (3,' 471 | } 472 | ]); 473 | 474 | t.end(); 475 | }); 476 | 477 | test('of', function(t) { 478 | t.deepEqual(v().of(['foo', 'bar'])('foo').errors, []); 479 | t.deepEqual(v().of(['foo'])('bar').errors, [ 480 | { 481 | value: 'bar', 482 | operator: 'of', 483 | expected: ['foo'], 484 | message: 'Expected "bar" to be of ["foo"]' 485 | } 486 | ]); 487 | t.deepEqual(v().of(['foo'], 'of')('bar').errors, [ 488 | { 489 | value: 'bar', 490 | operator: 'of', 491 | expected: ['foo'], 492 | message: 'of' 493 | } 494 | ]); 495 | t.end(); 496 | }); 497 | 498 | test('notOf', function(t) { 499 | t.deepEqual(v().notOf(['foo', 'bar'])('baz').errors, []); 500 | t.deepEqual(v().notOf(['foo'])('foo').errors, [ 501 | { 502 | value: 'foo', 503 | operator: 'notOf', 504 | expected: ['foo'], 505 | message: 'Expected "foo" not to be of ["foo"]' 506 | } 507 | ]); 508 | t.deepEqual(v().notOf(['foo'], 'not of')('foo').errors, [ 509 | { 510 | value: 'foo', 511 | operator: 'notOf', 512 | expected: ['foo'], 513 | message: 'not of' 514 | } 515 | ]); 516 | t.end(); 517 | }); 518 | 519 | test('integration', function(t) { 520 | t.test(function(t){ 521 | t.deepEqual(v().of(['foo']).len(3).match(/^foo$/) 522 | .notEqual('bar').equal('foo').string()('foo').errors, []); 523 | t.end(); 524 | }); 525 | t.test(function(t){ 526 | var res = v() 527 | .match(/\d/, 'A password must contain at least one number') 528 | .match(/[a-z]/, 'A password must contain at least one lowercase letter') 529 | .match(/[A-Z]/, 'A password must contain at least one uppercase letter') 530 | .match(/!@#$%^&/, 'A passwould must contain at least one of the following "!@#$%^&"') 531 | .len({ gte: 8, lte: 128 }, 'A password contain between 8 and 128 characters')(''); 532 | t.equal(res.errors.length, 5); 533 | t.end(); 534 | }); 535 | t.end(); 536 | }); 537 | 538 | test('putin', function(t) { 539 | v.putin('foo', 'bar', function(err){ 540 | t.equal(err.message, 'in node, error throws you!'); 541 | t.end(); 542 | }); 543 | }); 544 | 545 | 546 | test('custom', function(t) { 547 | var trueCheck = function() {}; 548 | var falseCheck = function() { return 'Some error found'; }; 549 | 550 | t.deepEqual(v().custom(trueCheck)(1).errors, []); 551 | t.deepEqual(v().custom(falseCheck)(1).errors, ['Some error found']); 552 | t.end(); 553 | }); 554 | --------------------------------------------------------------------------------