Comparing version 4.2.1 to 4.3.0
@@ -21,3 +21,3 @@ // Load modules | ||
this._inner = []; | ||
this._inner.matches = []; | ||
}; | ||
@@ -31,4 +31,4 @@ | ||
var errors = []; | ||
for (var i = 0, il = this._inner.length; i < il; ++i) { | ||
var item = this._inner[i]; | ||
for (var i = 0, il = this._inner.matches.length; i < il; ++i) { | ||
var item = this._inner.matches[i]; | ||
var schema = item.schema; | ||
@@ -68,3 +68,3 @@ if (!schema) { | ||
} | ||
obj._inner.push({ schema: cast }); | ||
obj._inner.matches.push({ schema: cast }); | ||
} | ||
@@ -81,3 +81,3 @@ | ||
Hoek.assert(typeof options === 'object', 'Invalid options'); | ||
Hoek.assert(options.is, 'Missing "is" directive'); | ||
Hoek.assert(options.hasOwnProperty('is'), 'Missing "is" directive'); | ||
Hoek.assert(options.then !== undefined || options.otherwise !== undefined, 'options must have at least one of "then" or "otherwise"'); | ||
@@ -105,3 +105,3 @@ | ||
obj._inner.push(item); | ||
obj._inner.matches.push(item); | ||
@@ -115,4 +115,4 @@ return obj; | ||
var descriptions = []; | ||
for (var i = 0, il = this._inner.length; i < il; ++i) { | ||
var item = this._inner[i]; | ||
for (var i = 0, il = this._inner.matches.length; i < il; ++i) { | ||
var item = this._inner.matches[i]; | ||
if (item.schema) { | ||
@@ -119,0 +119,0 @@ |
122
lib/any.js
@@ -32,6 +32,17 @@ // Load modules | ||
this._settings = null; | ||
this._valids = new internals.Set([undefined]); | ||
this._invalids = new internals.Set([null]); | ||
this._valids = new internals.Set(); | ||
this._invalids = new internals.Set(); | ||
this._tests = []; | ||
this._flags = {}; // insensitive (false), allowOnly (false), default (undefined), encoding (undefined), allowUnknown (undefined), forbidden (false) | ||
this._refs = []; | ||
this._flags = { /* | ||
presence: 'optional', // optional, required, forbidden, ignore | ||
allowOnly: false, | ||
allowUnknown: undefined, | ||
default: undefined, | ||
forbidden: false, | ||
encoding: undefined, | ||
insensitive: false, | ||
trim: false, | ||
case: undefined // upper, lower | ||
*/}; | ||
@@ -43,7 +54,5 @@ this._description = null; | ||
this._examples = []; | ||
this._meta = []; | ||
this._inner = null; | ||
this._renames = []; | ||
this._dependencies = []; | ||
this._refs = []; | ||
this._inner = {}; // Hash of arrays of immutable objects | ||
}; | ||
@@ -63,2 +72,3 @@ | ||
obj._tests = this._tests.slice(); | ||
obj._refs = this._refs.slice(); | ||
obj._flags = Hoek.clone(this._flags); | ||
@@ -71,7 +81,10 @@ | ||
obj._examples = this._examples.slice(); | ||
obj._meta = this._meta.slice(); | ||
obj._inner = this._inner ? this._inner.slice() : null; | ||
obj._renames = this._renames.slice(); | ||
obj._dependencies = this._dependencies.slice(); | ||
obj._refs = this._refs.slice(); | ||
obj._inner = {}; | ||
var inners = Object.keys(this._inner); | ||
for (var i = 0, il = inners.length; i < il; ++i) { | ||
var key = inners[i]; | ||
obj._inner[key] = this._inner[key] ? this._inner[key].slice() : null; | ||
} | ||
@@ -90,5 +103,6 @@ return obj; | ||
obj._settings = obj._settings ? internals.concatSettings(obj._settings, schema._settings) : schema._settings; | ||
obj._valids.merge(schema._valids); | ||
obj._invalids.merge(schema._invalids); | ||
obj._valids.merge(schema._valids, schema._invalids); | ||
obj._invalids.merge(schema._invalids, schema._valids); | ||
obj._tests = obj._tests.concat(schema._tests); | ||
obj._refs = obj._refs.concat(schema._refs); | ||
Hoek.merge(obj._flags, schema._flags); | ||
@@ -101,7 +115,11 @@ | ||
obj._examples = obj._examples.concat(schema._examples); | ||
obj._meta = obj._meta.concat(schema._meta); | ||
obj._inner = obj._inner ? (schema._inner ? obj._inner.concat(schema._inner) : obj._inner) : schema._inner; | ||
obj._renames = obj._renames.concat(schema._renames); | ||
obj._dependencies = obj._dependencies.concat(schema._dependencies); | ||
obj._refs = obj._refs.concat(schema._refs); | ||
var inners = Object.keys(schema._inner); | ||
for (var i = 0, il = inners.length; i < il; ++i) { | ||
var key = inners[i]; | ||
if (schema._inner[key]) { | ||
obj._inner[key] = (obj._inner[key] ? obj._inner[key].concat(schema._inner[key]) : schema._inner[key].slice()); | ||
} | ||
} | ||
@@ -187,4 +205,3 @@ return obj; | ||
var obj = this.clone(); | ||
obj._valids.remove(undefined); | ||
obj._invalids.add(undefined); | ||
obj._flags.presence = 'required'; | ||
return obj; | ||
@@ -197,4 +214,3 @@ }; | ||
var obj = this.clone(); | ||
obj._invalids.remove(undefined); | ||
obj._valids.add(undefined); | ||
delete obj._flags.presence; // Defaults to 'optional' | ||
return obj; | ||
@@ -216,3 +232,3 @@ }; | ||
var obj = this.clone(); | ||
obj._flags.forbidden = true; | ||
obj._flags.presence = 'forbidden'; | ||
return obj; | ||
@@ -232,3 +248,5 @@ }; | ||
Alternatives = Alternatives || require('./alternatives'); | ||
return Alternatives.when(ref, { is: options.is, then: then, otherwise: otherwise }); | ||
var obj = Alternatives.when(ref, { is: options.is, then: then, otherwise: otherwise }); | ||
obj._flags.presence = 'ignore'; | ||
return obj; | ||
}; | ||
@@ -266,3 +284,12 @@ | ||
internals.Any.prototype.meta = function (meta) { | ||
Hoek.assert(meta !== undefined, 'Meta cannot be undefined'); | ||
var obj = this.clone(); | ||
obj._meta = obj._meta.concat(meta); | ||
return obj; | ||
}; | ||
internals.Any.prototype.example = function (value) { | ||
@@ -311,10 +338,25 @@ | ||
// Check forbidden | ||
// Check presence requirements | ||
if (this._flags.forbidden && | ||
value !== undefined) { | ||
if (this._flags.presence) { // 'required', 'forbidden', or 'ignore' | ||
if (this._flags.presence === 'required' && | ||
value === undefined) { | ||
errors.push(Errors.create('any.unknown', null, state, options)); | ||
return finish(); | ||
errors.push(Errors.create('any.required', null, state, options)); | ||
return finish(); | ||
} | ||
else if (this._flags.presence === 'forbidden') { | ||
if (value === undefined) { | ||
return finish(); | ||
} | ||
errors.push(Errors.create('any.unknown', null, state, options)); | ||
return finish(); | ||
} | ||
} | ||
else { // 'optional' | ||
if (value === undefined) { | ||
return finish(); | ||
} | ||
} | ||
@@ -328,3 +370,3 @@ // Check allowed and denied values using the original value | ||
if (this._invalids.has(value, state, options, this._flags.insensitive)) { | ||
errors.push(Errors.create(value === '' ? 'any.empty' : (value === undefined ? 'any.required' : 'any.invalid'), null, state, options)); | ||
errors.push(Errors.create(value === '' ? 'any.empty' : 'any.invalid', null, state, options)); | ||
if (options.abortEarly || | ||
@@ -440,2 +482,6 @@ value === undefined) { // No reason to keep validating missing value | ||
if (this._meta.length) { | ||
description.meta = this._meta; | ||
} | ||
if (this._examples.length) { | ||
@@ -480,9 +526,5 @@ description.examples = this._examples; | ||
internals.Set = function (values) { | ||
internals.Set = function () { | ||
this._set = []; | ||
for (var i = 0, il = values.length; i < il; ++i) { | ||
this.add(values[i]); | ||
} | ||
}; | ||
@@ -506,7 +548,11 @@ | ||
internals.Set.prototype.merge = function (set) { | ||
internals.Set.prototype.merge = function (add, remove) { | ||
for (var i = 0, il = set._set.length; i < il; ++i) { | ||
this.add(set._set[i]); | ||
for (var i = 0, il = add._set.length; i < il; ++i) { | ||
this.add(add._set[i]); | ||
} | ||
for (i = 0, il = remove._set.length; i < il; ++i) { | ||
this.remove(remove._set[i]); | ||
} | ||
}; | ||
@@ -624,2 +670,2 @@ | ||
return obj; | ||
}; | ||
}; |
110
lib/array.js
@@ -19,2 +19,4 @@ // Load modules | ||
this._type = 'array'; | ||
this._inner.inclusions = []; | ||
this._inner.exclusions = []; | ||
}; | ||
@@ -43,25 +45,31 @@ | ||
result.errors = Array.isArray(result.value) ? null : Errors.create('array.base', null, state, options); | ||
return result; | ||
}; | ||
if (!Array.isArray(result.value)) { | ||
result.errors = Errors.create('array.base', null, state, options); | ||
return result; | ||
} | ||
if (this._inner.inclusions.length || | ||
this._inner.exclusions.length) { | ||
internals.Array.prototype.includes = function () { | ||
for (var v = 0, vl = result.value.length; v < vl; ++v) { | ||
var item = result.value[v]; | ||
var isValid = false; | ||
var localState = { key: v, path: (state.path ? state.path + '.' : '') + v, parent: result.value, reference: state.reference }; | ||
var inclusions = Hoek.flatten(Array.prototype.slice.call(arguments)).map(function (type) { | ||
// Exclusions | ||
return Cast.schema(type); | ||
}); | ||
for (var i = 0, il = this._inner.exclusions.length; i < il; ++i) { | ||
var res = this._inner.exclusions[i]._validate(item, localState, {}); // Not passing options to use defaults | ||
if (!res.errors) { | ||
result.errors = Errors.create('array.excludes', { pos: v }, { key: state.key, path: localState.path }, options); | ||
return result; | ||
} | ||
} | ||
return this._test('includes', inclusions, function (value, state, options) { | ||
// Inclusions | ||
for (var v = 0, vl = value.length; v < vl; ++v) { | ||
var item = value[v]; | ||
var isValid = false; | ||
var localState = { key: v, path: (state.path ? state.path + '.' : '') + v, parent: value, reference: state.reference }; | ||
for (var i = 0, il = inclusions.length; i < il; ++i) { | ||
var result = inclusions[i]._validate(item, localState, options); | ||
if (!result.errors) { | ||
value[v] = result.value; | ||
for (i = 0, il = this._inner.inclusions.length; i < il; ++i) { | ||
var res = this._inner.inclusions[i]._validate(item, localState, options); | ||
if (!res.errors) { | ||
result.value[v] = res.value; | ||
isValid = true; | ||
@@ -74,39 +82,67 @@ break; | ||
if (il === 1) { | ||
return Errors.create('array.includesOne', { pos: v, reason: result.errors }, { key: state.key, path: localState.path }, options); | ||
result.errors = Errors.create('array.includesOne', { pos: v, reason: res.errors }, { key: state.key, path: localState.path }, options); | ||
return result; | ||
} | ||
} | ||
if (!isValid) { | ||
return Errors.create('array.includes', { pos: v }, { key: state.key, path: localState.path }, options); | ||
if (this._inner.inclusions.length && | ||
!isValid) { | ||
result.errors = Errors.create('array.includes', { pos: v }, { key: state.key, path: localState.path }, options); | ||
return result; | ||
} | ||
} | ||
} | ||
return null; | ||
}); | ||
return result; | ||
}; | ||
internals.Array.prototype.excludes = function () { | ||
internals.Array.prototype.describe = function () { | ||
var exclusions = Hoek.flatten(Array.prototype.slice.call(arguments)).map(function (type) { | ||
var description = Any.prototype.describe.call(this); | ||
if (this._inner.inclusions.length) { | ||
description.includes = []; | ||
for (var i = 0, il = this._inner.inclusions.length; i < il; ++i) { | ||
description.includes.push(this._inner.inclusions[i].describe()); | ||
} | ||
} | ||
if (this._inner.exclusions.length) { | ||
description.excludes = []; | ||
for (var i = 0, il = this._inner.exclusions.length; i < il; ++i) { | ||
description.excludes.push(this._inner.exclusions[i].describe()); | ||
} | ||
} | ||
return description; | ||
}; | ||
internals.Array.prototype.includes = function () { | ||
var inclusions = Hoek.flatten(Array.prototype.slice.call(arguments)).map(function (type) { | ||
return Cast.schema(type); | ||
}); | ||
return this._test('excludes', exclusions, function (value, state, options) { | ||
var obj = this.clone(); | ||
obj._inner.inclusions = obj._inner.inclusions.concat(inclusions); | ||
return obj; | ||
}; | ||
for (var v = 0, vl = value.length; v < vl; ++v) { | ||
var item = value[v]; | ||
var localState = { key: v, path: (state.path ? state.path + '.' : '') + v, parent: value, reference: state.reference }; | ||
for (var i = 0, il = exclusions.length; i < il; ++i) { | ||
var result = exclusions[i]._validate(item, localState, {}); // Not passing options to use defaults | ||
if (!result.errors) { | ||
return Errors.create('array.excludes', { pos: v }, { key: state.key, path: localState.path }, options); | ||
} | ||
} | ||
} | ||
internals.Array.prototype.excludes = function () { | ||
return null; | ||
var exclusions = Hoek.flatten(Array.prototype.slice.call(arguments)).map(function (type) { | ||
return Cast.schema(type); | ||
}); | ||
var obj = this.clone(); | ||
obj._inner.exclusions = obj._inner.exclusions.concat(exclusions); | ||
return obj; | ||
}; | ||
@@ -160,2 +196,2 @@ | ||
module.exports = new internals.Array(); | ||
module.exports = new internals.Array(); |
@@ -45,2 +45,4 @@ // Load modules | ||
Hoek.assert(Buffer.isEncoding(encoding), 'Invalid encoding:', encoding); | ||
var obj = this.clone(); | ||
@@ -47,0 +49,0 @@ obj._flags.encoding = encoding; |
@@ -28,3 +28,3 @@ // Load modules | ||
result.errors = (result.value instanceof Date) ? null : Errors.create('date.base', null, state, options); | ||
result.errors = (result.value instanceof Date && !isNaN(result.value.getTime())) ? null : Errors.create('date.base', null, state, options); | ||
return result; | ||
@@ -31,0 +31,0 @@ }; |
@@ -85,4 +85,7 @@ // Load modules | ||
guid: 'must be a valid GUID', | ||
hostname: 'must be a valid hostname' | ||
hostname: 'must be a valid hostname', | ||
lowercase: 'must only contain lowercase characters', | ||
uppercase: 'must only contain uppercase characters', | ||
trim: 'must not have leading or trailing whitespace' | ||
} | ||
}; |
@@ -20,3 +20,6 @@ // Load modules | ||
this._type = 'object'; | ||
this._inner = null; | ||
this._inner.children = null; | ||
this._inner.renames = []; | ||
this._inner.dependencies = []; | ||
this._inner.patterns = []; | ||
}; | ||
@@ -73,4 +76,4 @@ | ||
var renamed = {}; | ||
for (var r = 0, rl = this._renames.length; r < rl; ++r) { | ||
var item = this._renames[r]; | ||
for (var r = 0, rl = this._inner.renames.length; r < rl; ++r) { | ||
var item = this._inner.renames[r]; | ||
@@ -104,7 +107,7 @@ if (!item.options.multiple && | ||
// Helper.validate dependencies | ||
// Validate dependencies | ||
for (var d = 0, dl = this._dependencies.length; d < dl; ++d) { | ||
var dep = this._dependencies[d]; | ||
var err = internals[dep.type](dep.key && value[dep.key], dep.peers, target, { key: dep.key, path: (state.path ? state.path + '.' : '') + dep.key }, options); | ||
for (var d = 0, dl = this._inner.dependencies.length; d < dl; ++d) { | ||
var dep = this._inner.dependencies[d]; | ||
var err = internals[dep.type](dep.key !== null && value[dep.key], dep.peers, target, { key: dep.key, path: (state.path ? state.path + '.' : '') + dep.key }, options); | ||
if (err) { | ||
@@ -118,5 +121,5 @@ errors.push(err); | ||
// Helper.validate schema | ||
// Validate schema | ||
if (!this._inner) { // null allows any keys | ||
if (!this._inner.children) { // null allows any keys | ||
return finish(); | ||
@@ -128,4 +131,4 @@ } | ||
for (var i = 0, il = this._inner.length; i < il; ++i) { | ||
var child = this._inner[i]; | ||
for (var i = 0, il = this._inner.children.length; i < il; ++i) { | ||
var child = this._inner.children[i]; | ||
var key = child.key; | ||
@@ -150,3 +153,37 @@ var item = target[key]; | ||
// Unknown keys | ||
var unprocessedKeys = Object.keys(unprocessed); | ||
if (unprocessedKeys.length && | ||
this._inner.patterns.length) { | ||
for (i = 0, il = unprocessedKeys.length; i < il; ++i) { | ||
var key = unprocessedKeys[i]; | ||
for (var p = 0, pl = this._inner.patterns.length; p < pl; ++p) { | ||
var pattern = this._inner.patterns[p]; | ||
if (pattern.regex.test(key)) { | ||
delete unprocessed[key]; | ||
var item = target[key]; | ||
var localState = { key: key, path: (state.path ? state.path + '.' : '') + key, parent: target, reference: state.reference }; | ||
var result = pattern.rule._validate(item, localState, options); | ||
if (result.errors) { | ||
errors = errors.concat(result.errors); | ||
if (options.abortEarly) { | ||
return finish(); | ||
} | ||
} | ||
if (result.value !== undefined) { | ||
target[key] = result.value; | ||
} | ||
} | ||
} | ||
} | ||
unprocessedKeys = Object.keys(unprocessed); | ||
} | ||
if (unprocessedKeys.length) { | ||
@@ -159,2 +196,3 @@ if (options.stripUnknown || | ||
key = unprocessedKeys[k]; | ||
if (options.stripUnknown) { | ||
@@ -199,3 +237,3 @@ delete target[key]; | ||
if (!schema) { | ||
obj._inner = null; | ||
obj._inner.children = null; | ||
return obj; | ||
@@ -207,3 +245,3 @@ } | ||
if (!children.length) { | ||
obj._inner = []; | ||
obj._inner.children = []; | ||
return obj; | ||
@@ -213,5 +251,5 @@ } | ||
var topo = new Topo(); | ||
if (obj._inner) { | ||
for (var i = 0, il = obj._inner.length; i < il; ++i) { | ||
var child = obj._inner[i]; | ||
if (obj._inner.children) { | ||
for (var i = 0, il = obj._inner.children.length; i < il; ++i) { | ||
var child = obj._inner.children[i]; | ||
topo.add(child, { after: child._refs, group: child.key }); | ||
@@ -228,3 +266,3 @@ } | ||
obj._inner = topo.nodes; | ||
obj._inner.children = topo.nodes; | ||
@@ -288,2 +326,13 @@ return obj; | ||
internals.Object.prototype.pattern = function (regex, schema) { | ||
Hoek.assert(regex instanceof RegExp, 'Invalid regular expression'); | ||
Hoek.assert(schema !== undefined, 'Invalid rule'); | ||
var obj = this.clone(); | ||
obj._inner.patterns.push({ regex: regex, rule: Cast.schema(schema) }); | ||
return obj; | ||
}; | ||
internals.Object.prototype.with = function (key, peers) { | ||
@@ -331,8 +380,8 @@ | ||
Hoek.assert(from, 'Rename missing the from argument'); | ||
Hoek.assert(to, 'Rename missing the to argument'); | ||
Hoek.assert(typeof from === 'string', 'Rename missing the from argument'); | ||
Hoek.assert(typeof to === 'string', 'Rename missing the to argument'); | ||
Hoek.assert(to !== from, 'Cannot rename key to same name:', from); | ||
for (var i = 0, il = this._renames.length; i < il; ++i) { | ||
Hoek.assert(this._renames[i].from !== from, 'Cannot rename the same key multiple times'); | ||
for (var i = 0, il = this._inner.renames.length; i < il; ++i) { | ||
Hoek.assert(this._inner.renames[i].from !== from, 'Cannot rename the same key multiple times'); | ||
} | ||
@@ -342,3 +391,3 @@ | ||
obj._renames.push({ | ||
obj._inner.renames.push({ | ||
from: from, | ||
@@ -361,3 +410,3 @@ to: to, | ||
var obj = this.clone(); | ||
obj._dependencies.push({ type: type, key: key, peers: peers }); | ||
obj._inner.dependencies.push({ type: type, key: key, peers: peers }); | ||
return obj; | ||
@@ -377,2 +426,3 @@ }; | ||
parent[peer] === undefined) { | ||
return Errors.create('object.with', { peer: peer }, state, options); | ||
@@ -465,10 +515,12 @@ } | ||
internals.Object.prototype.describe = function () { | ||
internals.Object.prototype.describe = function (shallow) { | ||
var description = Any.prototype.describe.call(this); | ||
if (this._inner) { | ||
if (this._inner.children && | ||
!shallow) { | ||
description.children = {}; | ||
for (var i = 0, il = this._inner.length; i < il; ++i) { | ||
var child = this._inner[i]; | ||
for (var i = 0, il = this._inner.children.length; i < il; ++i) { | ||
var child = this._inner.children[i]; | ||
description.children[child.key] = child.schema.describe(); | ||
@@ -478,4 +530,4 @@ } | ||
if (this._dependencies.length) { | ||
description.dependencies = Hoek.clone(this._dependencies); | ||
if (this._inner.dependencies.length) { | ||
description.dependencies = Hoek.clone(this._inner.dependencies); | ||
} | ||
@@ -506,2 +558,2 @@ | ||
module.exports = new internals.Object(); | ||
module.exports = new internals.Object(); |
// Load modules | ||
var Net = require('net'); | ||
var Hoek = require('hoek'); | ||
var Isemail = require('isemail'); | ||
var Any = require('./any'); | ||
var Errors = require('./errors'); | ||
var Hoek = require('hoek'); | ||
@@ -26,2 +27,14 @@ | ||
if (typeof value === 'string' && | ||
options.convert) { | ||
if (this._flags.case) { | ||
value = (this._flags.case === 'upper' ? value.toLocaleUpperCase() : value.toLocaleLowerCase()); | ||
} | ||
if (this._flags.trim) { | ||
value = value.trim(); | ||
} | ||
} | ||
return { | ||
@@ -45,2 +58,3 @@ value: value, | ||
Hoek.assert(Hoek.isInteger(limit) && limit >= 0, 'limit must be a positive integer'); | ||
Hoek.assert(!encoding || Buffer.isEncoding(encoding), 'Invalid encoding:', encoding); | ||
@@ -62,2 +76,3 @@ return this._test('min', limit, function (value, state, options) { | ||
Hoek.assert(Hoek.isInteger(limit) && limit >= 0, 'limit must be a positive integer'); | ||
Hoek.assert(!encoding || Buffer.isEncoding(encoding), 'Invalid encoding:', encoding); | ||
@@ -79,2 +94,3 @@ return this._test('max', limit, function (value, state, options) { | ||
Hoek.assert(Hoek.isInteger(limit) && limit >= 0, 'limit must be a positive integer'); | ||
Hoek.assert(!encoding || Buffer.isEncoding(encoding), 'Invalid encoding:', encoding); | ||
@@ -136,7 +152,5 @@ return this._test('length', limit, function (value, state, options) { | ||
var regex = /^(?:[\w\!\#\$\%\&\'\*\+\-\/\=\?\^\`\{\|\}\~]+\.)*[\w\!\#\$\%\&\'\*\+\-\/\=\?\^\`\{\|\}\~]+@(?:(?:(?:[a-zA-Z0-9](?:[a-zA-Z0-9\-](?!\.)){0,61}[a-zA-Z0-9]?\.)+[a-zA-Z0-9](?:[a-zA-Z0-9\-](?!$)){0,61}[a-zA-Z0-9]?)|(?:\[(?:(?:[01]?\d{1,2}|2[0-4]\d|25[0-5])\.){3}(?:[01]?\d{1,2}|2[0-4]\d|25[0-5])\]))$/; | ||
return this._test('email', undefined, function (value, state, options) { | ||
if (regex.test(value)) { | ||
if (Isemail(value)) { | ||
return null; | ||
@@ -198,2 +212,56 @@ } | ||
internals.String.prototype.lowercase = function () { | ||
var obj = this._test('lowercase', undefined, function (value, state, options) { | ||
if (options.convert || | ||
value === value.toLocaleLowerCase()) { | ||
return null; | ||
} | ||
return Errors.create('string.lowercase', null, state, options); | ||
}); | ||
obj._flags.case = 'lower'; | ||
return obj; | ||
}; | ||
internals.String.prototype.uppercase = function (options) { | ||
var obj = this._test('uppercase', undefined, function (value, state, options) { | ||
if (options.convert || | ||
value === value.toLocaleUpperCase()) { | ||
return null; | ||
} | ||
return Errors.create('string.uppercase', null, state, options); | ||
}); | ||
obj._flags.case = 'upper'; | ||
return obj; | ||
}; | ||
internals.String.prototype.trim = function () { | ||
var obj = this._test('trim', undefined, function (value, state, options) { | ||
if (options.convert || | ||
value === value.trim()) { | ||
return null; | ||
} | ||
return Errors.create('string.trim', null, state, options); | ||
}); | ||
obj._flags.trim = true; | ||
return obj; | ||
}; | ||
module.exports = new internals.String(); |
{ | ||
"name": "joi", | ||
"description": "Object schema validation", | ||
"version": "4.2.1", | ||
"version": "4.3.0", | ||
"repository": "git://github.com/spumko/joi", | ||
@@ -16,3 +16,4 @@ "main": "index", | ||
"hoek": "^2.1.x", | ||
"topo": "1.x.x" | ||
"topo": "1.x.x", | ||
"isemail": "0.1.x" | ||
}, | ||
@@ -19,0 +20,0 @@ "devDependencies": { |
@@ -6,3 +6,3 @@ <a href="https://github.com/spumko"><img src="https://raw.github.com/spumko/spumko/master/images/from.png" align="right" /></a> | ||
Current version: **4.2.x** | ||
Current version: **4.3.x** | ||
@@ -30,2 +30,3 @@ [![Build Status](https://secure.travis-ci.org/spumko/joi.png)](http://travis-ci.org/spumko/joi) | ||
- [`any.tags(tags)`](#anytagstags) | ||
- [`any.meta(meta)`](#anymetameta) | ||
- [`any.example(value)`](#anyexamplevalue) | ||
@@ -63,2 +64,3 @@ - [`any.unit(name)`](#anyunitname) | ||
- [`object.length(limit)`](#objectlengthlimit) | ||
- [`object.pattern(regex, schema)`](#objectpatternregex-schema) | ||
- [`object.and(peers)`](#objectandpeers) | ||
@@ -84,2 +86,5 @@ - [`object.or(peers)`](#objectorpeers) | ||
- [`string.hostname()`](#stringhostname) | ||
- [`string.lowercase()`](#stringlowercase) | ||
- [`string.uppercase()`](#stringuppercase) | ||
- [`string.trim()`](#stringtrim) | ||
- [`alternatives`](#alternatives) | ||
@@ -178,3 +183,3 @@ - [`alternatives.try(schemas)`](#alternativestryschemas) | ||
`validate()` and not using `any.options()`. | ||
- `callback` - the callback method using the signature `function(err, value)` where: | ||
- `callback` - the synchronous callback method using the signature `function(err, value)` where: | ||
- `err` - if validation failed, the error reason, otherwise `null`. | ||
@@ -326,2 +331,11 @@ - `value` - the validated value with any type conversions and other modifiers applied (the input is left unchanged). `value` can be | ||
#### `any.meta(meta)` | ||
Attaches metadata to the key where: | ||
- `meta` - the meta object to attach. | ||
```javascript | ||
var schema = Joi.any().meta({ index: true }); | ||
``` | ||
#### `any.example(value)` | ||
@@ -369,2 +383,5 @@ | ||
Note that if `value` is an object, any changes to the object after `default()` is called will change the reference | ||
and any future assignment. | ||
```javascript | ||
@@ -653,2 +670,14 @@ var schema = { | ||
#### `object.parrern(regex, schema)` | ||
Specify validation rules for unknown keys matching a pattern where: | ||
- `regex` - a regular expression tested against the unknown key names. | ||
- `schema` - the schema object matching keys much validate against. | ||
```javascrip | ||
var schema = Joi.object({ | ||
a: Joi.string() | ||
}).pattern(/\w\d/, Joi.boolean()); | ||
``` | ||
#### `object.and(peers)` | ||
@@ -878,2 +907,29 @@ | ||
#### `string.lowercase()` | ||
Requires the string value to be all lowercase. If the validation `convert` option is on (enabled by default), the string | ||
will be forced to lowercase. | ||
```javascript | ||
var schema = Joi.string().lowercase(); | ||
``` | ||
#### `string.uppercase()` | ||
Requires the string value to be all uppercase. If the validation `convert` option is on (enabled by default), the string | ||
will be forced to uppercase. | ||
```javascript | ||
var schema = Joi.string().uppercase(); | ||
``` | ||
#### `string.trim()` | ||
Requires the string value to contain no whitespace before or after. If the validation `convert` option is on (enabled by | ||
default), the string will be trimmed. | ||
```javascript | ||
var schema = Joi.string().trim(); | ||
``` | ||
### `alternatives` | ||
@@ -905,8 +961,8 @@ | ||
Adds a conditional alternative schema type based on another key value where: | ||
Adds a conditional alternative schema type based on another key (not the same as `any.when()`) value where: | ||
- `ref` - the key name or [reference](#refkey-options). | ||
- `options` - an object with: | ||
- `is` - the required condition **joi** type. | ||
- `then` - the alternative schema type if the condition is true. Required if `otherwise` is missing. | ||
- `otherwise` - the alternative schema type if the condition is false. Required if `then` is missing. | ||
- `then` - the alternative schema type to **try** if the condition is true. Required if `otherwise` is missing. | ||
- `otherwise` - the alternative schema type to **try** if the condition is false. Required if `then` is missing. | ||
@@ -920,2 +976,27 @@ ```javascript | ||
Note that `when()` only adds additional alternatives to try and does not impact the overall type. Setting | ||
a `required()` rule on a single alternative will not apply to the overall key. For example, | ||
this definition of `a`: | ||
```javascript | ||
var schema = { | ||
a: Joi.alternatives().when('b', { is: true, then: Joi.required() }), | ||
b: Joi.boolean() | ||
}; | ||
``` | ||
Does not turn `a` into a required key when `b` is `true`. Instead, it tells the validator to try and match the | ||
value to anything that's not `undefined`. However, since `Joi.alternatives()` by itself allows `undefined`, the rule | ||
does not accomplish turning `a` to a required value. This rule is the same as `Joi.alternatives([Joi.required()])` | ||
when `b` is `true` which will allow any value including `undefined`. | ||
To accomplish the desired result above use: | ||
```javascript | ||
var schema = { | ||
a: Joi.when('b', { is: true, then: Joi.required() }), | ||
b: Joi.boolean() | ||
}; | ||
``` | ||
### `ref(key, [options])` | ||
@@ -922,0 +1003,0 @@ |
@@ -209,2 +209,15 @@ // Load modules | ||
}); | ||
it('validates when empty value', function (done) { | ||
var schema = { | ||
a: Joi.alternatives().when('b', { is: true, then: Joi.required() }), | ||
b: Joi.boolean().default(false) | ||
}; | ||
Helper.validate(schema, [ | ||
[{ b: false }, true], | ||
[{ b: true }, true] // true because required() only applies to the one alternative | ||
], done); | ||
}); | ||
}); | ||
@@ -224,9 +237,5 @@ | ||
type: 'object', | ||
valids: [undefined], | ||
invalids: [null], | ||
children: { | ||
b: { | ||
type: 'any', | ||
valids: [undefined], | ||
invalids: [null] | ||
type: 'any' | ||
}, | ||
@@ -241,5 +250,3 @@ a: [ | ||
}, | ||
valids: [undefined, 5 | ||
], | ||
invalids: [null] | ||
valids: [5] | ||
}, | ||
@@ -251,4 +258,4 @@ then: { | ||
}, | ||
valids: [undefined, 'x'], | ||
invalids: [null, ''] | ||
valids: ['x'], | ||
invalids: [''] | ||
}, | ||
@@ -260,4 +267,4 @@ otherwise: { | ||
}, | ||
valids: [undefined, 'y'], | ||
invalids: [null, ''] | ||
valids: ['y'], | ||
invalids: [''] | ||
} | ||
@@ -270,4 +277,4 @@ }, | ||
}, | ||
valids: [undefined, 'z'], | ||
invalids: [null, ''] | ||
valids: ['z'], | ||
invalids: [''] | ||
} | ||
@@ -292,9 +299,5 @@ ] | ||
type: 'object', | ||
valids: [undefined], | ||
invalids: [null], | ||
children: { | ||
b: { | ||
type: 'any', | ||
valids: [undefined], | ||
invalids: [null] | ||
type: 'any' | ||
}, | ||
@@ -309,5 +312,3 @@ a: [ | ||
}, | ||
valids: [undefined, 5 | ||
], | ||
invalids: [null] | ||
valids: [5] | ||
}, | ||
@@ -319,4 +320,4 @@ then: { | ||
}, | ||
valids: [undefined, 'x'], | ||
invalids: [null, ''] | ||
valids: ['x'], | ||
invalids: [''] | ||
} | ||
@@ -329,4 +330,4 @@ }, | ||
}, | ||
valids: [undefined, 'z'], | ||
invalids: [null, ''] | ||
valids: ['z'], | ||
invalids: [''] | ||
} | ||
@@ -351,9 +352,5 @@ ] | ||
type: 'object', | ||
valids: [undefined], | ||
invalids: [null], | ||
children: { | ||
b: { | ||
type: 'any', | ||
valids: [undefined], | ||
invalids: [null] | ||
type: 'any' | ||
}, | ||
@@ -368,5 +365,3 @@ a: [ | ||
}, | ||
valids: [undefined, 5 | ||
], | ||
invalids: [null] | ||
valids: [5] | ||
}, | ||
@@ -378,4 +373,4 @@ otherwise: { | ||
}, | ||
valids: [undefined, 'y'], | ||
invalids: [null, ''] | ||
valids: ['y'], | ||
invalids: [''] | ||
} | ||
@@ -388,4 +383,4 @@ }, | ||
}, | ||
valids: [undefined, 'z'], | ||
invalids: [null, ''] | ||
valids: ['z'], | ||
invalids: [''] | ||
} | ||
@@ -392,0 +387,0 @@ ] |
250
test/any.js
@@ -230,2 +230,28 @@ // Load modules | ||
describe('#meta', function () { | ||
it('sets the meta', function (done) { | ||
var meta = { prop: 'val', prop2: 3 }; | ||
var b = Joi.meta(meta); | ||
expect(b.describe().meta).to.deep.equal([meta]); | ||
b = b.meta({ other: true }); | ||
expect(b.describe().meta).to.deep.equal([meta, { | ||
other: true | ||
}]); | ||
done(); | ||
}); | ||
it('throws when meta is missing', function (done) { | ||
expect(function () { | ||
Joi.meta(); | ||
}).to.throw('Meta cannot be undefined'); | ||
done(); | ||
}); | ||
}); | ||
describe('#example', function () { | ||
@@ -329,11 +355,10 @@ | ||
[1, true], ['1', true] | ||
], function () { | ||
]); | ||
Helper.validate(a.concat(b), [ | ||
[1, true], ['1', false] | ||
], done); | ||
}); | ||
Helper.validate(a.concat(b), [ | ||
[1, true], ['1', false] | ||
], done); | ||
}); | ||
it('merges two schemas (invalid)', function (done) { | ||
it('merges two schemas (valid)', function (done) { | ||
@@ -344,14 +369,15 @@ var a = Joi.string().valid('a'); | ||
Helper.validate(a, [ | ||
['a', true], ['b', false] | ||
], function () { | ||
['a', true], | ||
['b', false] | ||
]); | ||
Helper.validate(b, [ | ||
['b', true], ['a', false] | ||
], function () { | ||
Helper.validate(b, [ | ||
['b', true], | ||
['a', false] | ||
]); | ||
Helper.validate(a.concat(b), [ | ||
['a', true], ['b', true] | ||
], done); | ||
}); | ||
}); | ||
Helper.validate(a.concat(b), [ | ||
['a', true], | ||
['b', true] | ||
], done); | ||
}); | ||
@@ -366,15 +392,34 @@ | ||
['b', true], ['a', false] | ||
], function () { | ||
]); | ||
Helper.validate(b, [ | ||
['a', true], ['b', false] | ||
], function () { | ||
Helper.validate(b, [ | ||
['a', true], ['b', false] | ||
]); | ||
Helper.validate(a.concat(b), [ | ||
['a', false], ['b', false] | ||
], done); | ||
}); | ||
}); | ||
Helper.validate(a.concat(b), [ | ||
['a', false], ['b', false] | ||
], done); | ||
}); | ||
it('merges two schemas (valid/invalid)', function (done) { | ||
var a = Joi.string().valid('a').invalid('b'); | ||
var b = Joi.string().valid('b').invalid('a'); | ||
Helper.validate(a, [ | ||
['a', true], | ||
['b', false] | ||
]); | ||
Helper.validate(b, [ | ||
['b', true], | ||
['a', false] | ||
]); | ||
Helper.validate(a.concat(b), [ | ||
['a', false], | ||
['b', true] | ||
], done); | ||
}); | ||
it('merges two schemas (tests)', function (done) { | ||
@@ -387,13 +432,11 @@ | ||
[4, false], [11, true] | ||
], function () { | ||
]); | ||
Helper.validate(b, [ | ||
[6, true], [11, false] | ||
], function () { | ||
Helper.validate(b, [ | ||
[6, true], [11, false] | ||
]); | ||
Helper.validate(a.concat(b), [ | ||
[4, false], [6, true], [11, false] | ||
], done); | ||
}); | ||
}); | ||
Helper.validate(a.concat(b), [ | ||
[4, false], [6, true], [11, false] | ||
], done); | ||
}); | ||
@@ -408,8 +451,7 @@ | ||
['a', true], ['A', false], ['b', false] | ||
], function () { | ||
]); | ||
Helper.validate(a.concat(b), [ | ||
['a', true], ['A', true], ['b', false] | ||
], done); | ||
}); | ||
Helper.validate(a.concat(b), [ | ||
['a', true], ['A', true], ['b', false] | ||
], done); | ||
}); | ||
@@ -428,5 +470,3 @@ | ||
examples: ['a', 'b'], | ||
unit: 'b', | ||
valids: [undefined], | ||
invalids: [null] | ||
unit: 'b' | ||
}); | ||
@@ -443,18 +483,15 @@ done(); | ||
[{ b: 1 }, true], [{ b: 2 }, true] | ||
], function () { | ||
]); | ||
Helper.validate(b, [ | ||
[{ b: 1 }, true], [{ b: 2 }, false] | ||
], function () { | ||
Helper.validate(b, [ | ||
[{ b: 1 }, true], [{ b: 2 }, false] | ||
]); | ||
Helper.validate(a.concat(b), [ | ||
[{ b: 1 }, true], [{ b: 2 }, false] | ||
], function () { | ||
Helper.validate(a.concat(b), [ | ||
[{ b: 1 }, true], [{ b: 2 }, false] | ||
]); | ||
Helper.validate(b.concat(a), [ | ||
[{ b: 1 }, true], [{ b: 2 }, false] | ||
], done); | ||
}); | ||
}); | ||
}); | ||
Helper.validate(b.concat(a), [ | ||
[{ b: 1 }, true], [{ b: 2 }, false] | ||
], done); | ||
}); | ||
@@ -469,18 +506,15 @@ | ||
[{}, true], [{ b: 2 }, false] | ||
], function () { | ||
]); | ||
Helper.validate(b, [ | ||
[{}, true], [{ b: 2 }, true] | ||
], function () { | ||
Helper.validate(b, [ | ||
[{}, true], [{ b: 2 }, true] | ||
]); | ||
Helper.validate(a.concat(b), [ | ||
[{}, true], [{ b: 2 }, false] | ||
], function () { | ||
Helper.validate(a.concat(b), [ | ||
[{}, true], [{ b: 2 }, false] | ||
]); | ||
Helper.validate(b.concat(a), [ | ||
[{}, true], [{ b: 2 }, false] | ||
], done); | ||
}); | ||
}); | ||
}); | ||
Helper.validate(b.concat(a), [ | ||
[{}, true], [{ b: 2 }, false] | ||
], done); | ||
}); | ||
@@ -495,18 +529,15 @@ | ||
[{ a: 1 }, true], [{ b: 2 }, false] | ||
], function () { | ||
]); | ||
Helper.validate(b, [ | ||
[{ a: 1 }, false], [{ b: 2 }, true] | ||
], function () { | ||
Helper.validate(b, [ | ||
[{ a: 1 }, false], [{ b: 2 }, true] | ||
]); | ||
Helper.validate(a.concat(b), [ | ||
[{ a: 1 }, true], [{ b: 2 }, true] | ||
], function () { | ||
Helper.validate(a.concat(b), [ | ||
[{ a: 1 }, true], [{ b: 2 }, true] | ||
]); | ||
Helper.validate(b.concat(a), [ | ||
[{ a: 1 }, true], [{ b: 2 }, true] | ||
], done); | ||
}); | ||
}); | ||
}); | ||
Helper.validate(b.concat(a), [ | ||
[{ a: 1 }, true], [{ b: 2 }, true] | ||
], done); | ||
}); | ||
@@ -554,2 +585,19 @@ | ||
}); | ||
it('merges meta properly', function (done) { | ||
var metaA = { a: 1 }; | ||
var metaB = { b: 1 }; | ||
var a = Joi.any().meta(metaA); | ||
var b = Joi.any().meta(metaB); | ||
var c = Joi.any(); | ||
var d = Joi.any(); | ||
expect(a.concat(b)._meta).to.deep.equal([{ a: 1 }, { b: 1 }]); | ||
expect(a.concat(c)._meta).to.deep.equal([metaA]); | ||
expect(b.concat(c)._meta).to.deep.equal([metaB]); | ||
expect(c.concat(d)._meta).to.deep.equal([]); | ||
done(); | ||
}); | ||
}); | ||
@@ -624,2 +672,17 @@ | ||
}); | ||
it('makes peer required', function (done) { | ||
var schema = { | ||
a: Joi.when('b', { is: 5, then: Joi.required() }), | ||
b: Joi.any() | ||
}; | ||
Helper.validate(schema, [ | ||
[{ b: 5 }, false], | ||
[{ b: 6 }, true], | ||
[{ a: 'b' }, true], | ||
[{ b: 5, a: 'x' }, true] | ||
], done) | ||
}); | ||
}); | ||
@@ -650,2 +713,21 @@ | ||
describe('#has', function () { | ||
it('compares date to null', function (done) { | ||
var any = Joi.any().clone(); | ||
any._valids.add(null); | ||
expect(any._valids.has(new Date())).to.equal(false); | ||
done(); | ||
}); | ||
it('compares buffer to null', function (done) { | ||
var any = Joi.any().clone(); | ||
any._valids.add(null); | ||
expect(any._valids.has(new Buffer(''))).to.equal(false); | ||
done(); | ||
}); | ||
}); | ||
describe('#values', function () { | ||
@@ -655,4 +737,4 @@ | ||
var a = Joi.any(); | ||
var b = a.required(); | ||
var a = Joi.any().valid('x').invalid('y'); | ||
var b = a.invalid('x'); | ||
expect(a._valids.values().length).to.equal(1); | ||
@@ -670,3 +752,3 @@ expect(b._valids.values().length).to.equal(0); | ||
var b = Joi.any(); | ||
var b = Joi.any().allow(undefined); | ||
expect(b._valids.toString(true)).to.equal('undefined'); | ||
@@ -673,0 +755,0 @@ done(); |
@@ -119,2 +119,16 @@ // Load modules | ||
}); | ||
it('validates multiple types added in two calls', function (done) { | ||
var schema = Joi.array() | ||
.includes(Joi.number()) | ||
.includes(Joi.string()); | ||
Helper.validate(schema, [ | ||
[[1, 2, 3], true], | ||
[[50, 100, 1000], true], | ||
[[1, 'a', 5, 10], true], | ||
[['joi', 'everydaylowprices', 5000], true] | ||
], done); | ||
}); | ||
}); | ||
@@ -237,3 +251,3 @@ | ||
it('should exclude values when excludes is called', function (done) { | ||
it('excludes values when excludes is called', function (done) { | ||
@@ -247,3 +261,3 @@ Helper.validate(Joi.array().excludes(Joi.string()), [ | ||
it('should allow types to be excluded', function (done) { | ||
it('allows types to be excluded', function (done) { | ||
@@ -266,3 +280,3 @@ var schema = Joi.array().excludes(Joi.number()); | ||
it('should validate array of Numbers', function (done) { | ||
it('validates array of Numbers', function (done) { | ||
@@ -272,7 +286,8 @@ Helper.validate(Joi.array().includes(Joi.number()), [ | ||
[[50, 100, 1000], true], | ||
[['a', 1, 2], false] | ||
[['a', 1, 2], false], | ||
[['1', '2', 4], true] | ||
], done); | ||
}); | ||
it('should validate array of mixed Numbers & Strings', function (done) { | ||
it('validates array of mixed Numbers & Strings', function (done) { | ||
@@ -287,3 +302,3 @@ Helper.validate(Joi.array().includes(Joi.number(), Joi.string()), [ | ||
it('should validate array of objects with schema', function (done) { | ||
it('validates array of objects with schema', function (done) { | ||
@@ -297,3 +312,3 @@ Helper.validate(Joi.array().includes(Joi.object({ h1: Joi.number().required() })), [ | ||
it('should not validate array of unallowed mixed types (Array)', function (done) { | ||
it('errors on array of unallowed mixed types (Array)', function (done) { | ||
@@ -334,3 +349,39 @@ Helper.validate(Joi.array().includes(Joi.number()), [ | ||
}); | ||
describe('#describe', function () { | ||
it('returns an empty description when no rules are applied', function (done) { | ||
var schema = Joi.array(); | ||
var desc = schema.describe(); | ||
expect(desc).to.deep.equal({ | ||
type: 'array' | ||
}); | ||
done(); | ||
}); | ||
it('returns an includes array only if includes are specified', function (done) { | ||
var schema = Joi.array().includes().max(5); | ||
var desc = schema.describe(); | ||
expect(desc.includes).to.not.exist; | ||
done(); | ||
}); | ||
it('returns a recursively defined array of includes when specified', function (done) { | ||
var schema = Joi.array().includes(Joi.number(), Joi.string()).excludes(Joi.boolean()); | ||
var desc = schema.describe(); | ||
expect(desc.includes).to.have.length(2); | ||
expect(desc.excludes).to.have.length(1); | ||
expect(desc).to.deep.equal({ | ||
type: 'array', | ||
includes: [{ type: 'number' }, { type: 'string', invalids: [''] }], | ||
excludes: [{ type: 'boolean' }] | ||
}); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
}); |
@@ -92,2 +92,11 @@ // Load modules | ||
}); | ||
it('throws when encoding is invalid', function (done) { | ||
expect(function () { | ||
Joi.binary().encoding('base6'); | ||
}).to.throw('Invalid encoding: base6'); | ||
done(); | ||
}); | ||
}); | ||
@@ -94,0 +103,0 @@ |
@@ -83,3 +83,12 @@ // Load modules | ||
}); | ||
it('validates only valid dates', function (done) { | ||
Helper.validate(Joi.date(), [ | ||
['1-1-2013', true], | ||
['not a valid date', false], | ||
[new Date('not a valid date'), false] | ||
], done); | ||
}); | ||
}); | ||
}); | ||
}); |
@@ -71,3 +71,3 @@ // Load modules | ||
expect(err).to.exist; | ||
expect(err.annotate()).to.equal('{\n \u001b[41m\"value\"\u001b[0m\u001b[31m [1]: -- missing --\u001b[0m\n}\n\u001b[31m\n[1] value contains an invalid value\u001b[0m'); | ||
expect(err.annotate()).to.equal('{\n \u001b[41m\"value\"\u001b[0m\u001b[31m [1]: -- missing --\u001b[0m\n}\n\u001b[31m\n[1] value must be a string\u001b[0m'); | ||
done(); | ||
@@ -1291,30 +1291,20 @@ }); | ||
type: 'object', | ||
valids: [undefined], | ||
invalids: [null], | ||
children: { | ||
sub: { | ||
type: 'object', | ||
valids: [undefined], | ||
invalids: [null], | ||
children: { | ||
email: { | ||
type: 'string', | ||
valids: [undefined], | ||
invalids: [null, ''], | ||
invalids: [''], | ||
rules: [{ name: 'email' }] | ||
}, | ||
date: { | ||
type: 'date', | ||
valids: [undefined], | ||
invalids: [null] | ||
type: 'date' | ||
}, | ||
child: { | ||
type: 'object', | ||
valids: [undefined], | ||
invalids: [null], | ||
children: { | ||
alphanum: { | ||
type: 'string', | ||
valids: [undefined], | ||
invalids: [null, ''], | ||
invalids: [''], | ||
rules: [{ name: 'alphanum' }] | ||
@@ -1328,10 +1318,7 @@ } | ||
{ | ||
type: 'number', | ||
valids: [undefined], | ||
invalids: [null] | ||
type: 'number' | ||
}, | ||
{ | ||
type: 'string', | ||
valids: [undefined], | ||
invalids: [null, ''], | ||
invalids: [''], | ||
rules: [{ name: 'min', arg: 3 }] | ||
@@ -1342,4 +1329,3 @@ } | ||
type: 'string', | ||
valids: [undefined], | ||
invalids: [null, ''], | ||
invalids: [''], | ||
rules: [{ name: 'max', arg: 3 }] | ||
@@ -1349,8 +1335,10 @@ }, | ||
type: 'string', | ||
invalids: [null, '', undefined] | ||
flags: { | ||
presence: 'required' | ||
}, | ||
invalids: [ ''] | ||
}, | ||
xor: { | ||
type: 'string', | ||
valids: [undefined], | ||
invalids: [null, ''] | ||
invalids: [''] | ||
}, | ||
@@ -1362,11 +1350,14 @@ renamed: { | ||
}, | ||
valids: [undefined, '456'], | ||
invalids: [null, ''] | ||
valids: ['456'], | ||
invalids: [''] | ||
}, | ||
notEmpty: { | ||
type: 'string', | ||
flags: { | ||
presence: 'required' | ||
}, | ||
description: 'a', | ||
notes: ['b'], | ||
tags: ['c'], | ||
invalids: [null, '', undefined] | ||
invalids: [''] | ||
} | ||
@@ -1407,5 +1398,3 @@ }, | ||
expect(description).to.deep.equal({ | ||
type: 'any', | ||
valids: [undefined], | ||
invalids: [null] | ||
type: 'any' | ||
}); | ||
@@ -1412,0 +1401,0 @@ done(); |
@@ -540,2 +540,18 @@ // Load modules | ||
}); | ||
it('should be able to rename keys that are empty strings', function (done) { | ||
var schema = Joi.object().rename('', 'notEmpty'); | ||
var input = { | ||
'': 'something' | ||
}; | ||
schema.validate(input, function (err, value) { | ||
expect(err).to.not.exist; | ||
expect(value['']).to.not.exist; | ||
expect(value.notEmpty).to.equal('something'); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
@@ -550,8 +566,21 @@ | ||
expect(desc).to.deep.equal({ | ||
type: 'object', | ||
valids: [undefined], | ||
invalids: [null] | ||
type: 'object' | ||
}); | ||
done(); | ||
}); | ||
it('respects the shallow parameter', function (done) { | ||
var schema = Joi.object({ | ||
name: Joi.string(), | ||
child: Joi.object({ | ||
name: Joi.string() | ||
}) | ||
}); | ||
expect(schema.describe(true)).to.not.contain.key('children'); | ||
expect(schema.describe()).to.contain.key('children'); | ||
done(); | ||
}); | ||
}); | ||
@@ -595,2 +624,44 @@ | ||
describe('#pattern', function () { | ||
it('validates unknown keys using a pattern', function (done) { | ||
var schema = Joi.object({ | ||
a: Joi.number() | ||
}).pattern(/\d+/, Joi.boolean()).pattern(/\w\w+/, 'x'); | ||
Joi.validate({ bb: 'y', 5: 'x' }, schema, { abortEarly: false }, function (err, value) { | ||
expect(err).to.exist; | ||
expect(err.message).to.equal('5 must be a boolean. bb must be one of x'); | ||
Helper.validate(schema, [ | ||
[{ a: 5 }, true], | ||
[{ a: 'x' }, false], | ||
[{ b: 'x' }, false], | ||
[{ bb: 'x' }, true], | ||
[{ 5: 'x' }, false], | ||
[{ 5: false }, true], | ||
[{ 5: undefined }, true] | ||
], done) | ||
}); | ||
}); | ||
it('validates unknown keys using a pattern (nested)', function (done) { | ||
var schema = { | ||
x: Joi.object({ | ||
a: Joi.number() | ||
}).pattern(/\d+/, Joi.boolean()).pattern(/\w\w+/, 'x') | ||
}; | ||
Joi.validate({ x: { bb: 'y', 5: 'x' } }, schema, { abortEarly: false }, function (err, value) { | ||
expect(err).to.exist; | ||
expect(err.message).to.equal('5 must be a boolean. bb must be one of x'); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
describe('#with', function () { | ||
@@ -619,2 +690,11 @@ | ||
}); | ||
it('should validate correctly when key is an empty string', function (done) { | ||
var schema = Joi.object().with('', 'b'); | ||
Helper.validate(schema, [ | ||
[{ c: 'hi', d: 'there' }, true], | ||
]); | ||
done(); | ||
}); | ||
}); | ||
@@ -643,4 +723,15 @@ | ||
expect(error).to.equal(true); | ||
done(); | ||
}); | ||
it('should validate correctly when key is an empty string', function (done) { | ||
var schema = Joi.object().without('', 'b'); | ||
Helper.validate(schema, [ | ||
[{ a: 'hi', b: 'there' }, true] | ||
]); | ||
done(); | ||
}); | ||
}); | ||
@@ -647,0 +738,0 @@ |
@@ -239,2 +239,144 @@ // Load modules | ||
describe('#lowercase', function () { | ||
it('only allows strings that are entirely lowercase', function (done) { | ||
var schema = Joi.string().lowercase(); | ||
Helper.validateOptions(schema, [ | ||
['this is all lowercase', true], | ||
['5', true], | ||
['lower\tcase', true], | ||
['Uppercase', false], | ||
['MixEd cAsE', false], | ||
[1, false] | ||
], { convert: false }, done); | ||
}); | ||
it('coerce string to lowercase before validation', function (done) { | ||
var schema = Joi.string().lowercase(); | ||
schema.validate('UPPER TO LOWER', function (err, value) { | ||
expect(err).to.not.exist; | ||
expect(value).to.equal('upper to lower'); | ||
done(); | ||
}); | ||
}); | ||
it('should work in combination with a trim', function (done) { | ||
var schema = Joi.string().lowercase().trim(); | ||
Helper.validate(schema, [ | ||
[' abc', true], | ||
[' ABC', true], | ||
['ABC', true], | ||
[1, false] | ||
], done); | ||
}); | ||
}); | ||
describe('#uppercase', function () { | ||
it('only allow strings that are entirely uppercase', function (done) { | ||
var schema = Joi.string().uppercase(); | ||
Helper.validateOptions(schema, [ | ||
['THIS IS ALL UPPERCASE', true], | ||
['5', true], | ||
['UPPER\nCASE', true], | ||
['lOWERCASE', false], | ||
['MixEd cAsE', false], | ||
[1, false] | ||
], { convert: false }, done); | ||
}); | ||
it('coerce string to uppercase before validation', function (done) { | ||
var schema = Joi.string().uppercase(); | ||
schema.validate('lower to upper', function (err, value) { | ||
expect(err).to.not.exist; | ||
expect(value).to.equal('LOWER TO UPPER'); | ||
done(); | ||
}); | ||
}); | ||
it('works in combination with a forced trim', function (done) { | ||
var schema = Joi.string().uppercase().trim(); | ||
Helper.validate(schema, [ | ||
[' abc', true], | ||
[' ABC', true], | ||
['ABC', true], | ||
[1, false] | ||
], done); | ||
}); | ||
}); | ||
describe('#trim', function () { | ||
it('only allow strings that have no leading or trailing whitespace', function (done) { | ||
var schema = Joi.string().trim(); | ||
Helper.validateOptions(schema, [ | ||
[' something', false], | ||
['something ', false], | ||
['something\n', false], | ||
['some thing', true], | ||
['something', true] | ||
], { convert: false }, done); | ||
}); | ||
it('removes leading and trailing whitespace before validation', function (done) { | ||
var schema = Joi.string().trim(); | ||
schema.validate(' trim this ', function (err, value) { | ||
expect(err).to.not.exist; | ||
expect(value).to.equal('trim this'); | ||
done(); | ||
}); | ||
}); | ||
it('should work in combination with min', function (done) { | ||
var schema = Joi.string().min(4).trim(); | ||
Helper.validate(schema, [ | ||
[' a ', false], | ||
['abc ', false], | ||
['abcd ', true] | ||
], done); | ||
}); | ||
it('should work in combination with max', function (done) { | ||
var schema = Joi.string().max(4).trim(); | ||
Helper.validate(schema, [ | ||
[' abcde ', false], | ||
['abc ', true], | ||
['abcd ', true] | ||
], done); | ||
}); | ||
it('should work in combination with length', function (done) { | ||
var schema = Joi.string().length(4).trim(); | ||
Helper.validate(schema, [ | ||
[' ab ', false], | ||
['abc ', false], | ||
['abcd ', true] | ||
], done); | ||
}); | ||
it('should work in combination with a case change', function (done) { | ||
var schema = Joi.string().trim().lowercase(); | ||
Helper.validate(schema, [ | ||
[' abc', true], | ||
[' ABC', true], | ||
['ABC', true] | ||
], done); | ||
}); | ||
}); | ||
describe('#validate', function () { | ||
@@ -406,4 +548,8 @@ | ||
Helper.validate(schema, [ | ||
['van@walmartlabs.com', true], | ||
['@iaminvalid.com', false] | ||
['joe@example.com', true], | ||
['"joe"@example.com', true], | ||
['@iaminvalid.com', false], | ||
['joe@[IPv6:2a00:1450:4001:c02::1b]', true], | ||
['12345678901234567890123456789012345678901234567890123456789012345@walmartlabs.com', false], | ||
['123456789012345678901234567890123456789012345678901234567890@12345678901234567890123456789012345678901234567890123456789.12345678901234567890123456789012345678901234567890123456789.12345678901234567890123456789012345678901234567890123456789.12345.toolong.com', false] | ||
], done); | ||
@@ -410,0 +556,0 @@ }); |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
458600
7172
1017
3
+ Addedisemail@0.1.x
+ Addedisemail@0.1.2(transitive)