fully-typed
Advanced tools
Comparing version 1.0.0 to 1.1.0
@@ -18,2 +18,3 @@ /** | ||
'use strict'; | ||
const crypto = require('crypto'); | ||
const util = require('./util'); | ||
@@ -32,2 +33,3 @@ | ||
const dependencies = new Map(); | ||
const instances = new WeakMap(); | ||
@@ -58,2 +60,3 @@ /** | ||
const normalizeFunctions = []; | ||
const firstStringAlias = aliases.filter(a => typeof a === 'string')[0]; | ||
@@ -73,6 +76,37 @@ // verify that inherits exist already and build inheritance arrays | ||
/** | ||
* Create a schema instance. | ||
* @param {object} config The configuration for the schema. | ||
* @param {object} schema The schema controller. | ||
* @constructor | ||
*/ | ||
function Schema(config, schema) { | ||
const length = ctrls.length; | ||
const extended = config.hasOwnProperty('__') ? config.__ : {}; | ||
this.Schema = schema; | ||
// apply controllers to this schema | ||
for (let i = 0; i < length; i++) ctrls[i].call(this, config); | ||
// add additional properties | ||
if (util.isPlainObject(extended.properties)) Object.defineProperties(this, extended.properties); | ||
// create a hash | ||
const protect = {}; | ||
const options = getNormalizedSchemaConfiguration(this); | ||
protect.hash = crypto | ||
.createHash('sha256') | ||
.update(Object.keys(options) | ||
.map(key => { | ||
const value = options[key]; | ||
if (typeof value === 'function') return value.toString(); | ||
if (typeof value === 'object') return JSON.stringify(value); | ||
return value; | ||
}) | ||
.join('') | ||
) | ||
.digest('hex'); | ||
// store the protected data | ||
instances.set(this, protect); | ||
} | ||
@@ -90,2 +124,6 @@ | ||
Schema.prototype.hash = function() { | ||
return instances.has(this) ? instances.get(this).hash : '' | ||
}; | ||
Schema.prototype.normalize = function(value) { | ||
@@ -101,2 +139,8 @@ if (typeof value === 'undefined' && this.hasDefault) value = this.default; | ||
Schema.prototype.toJSON = function() { | ||
const options = getNormalizedSchemaConfiguration(this); | ||
if (typeof options.type === 'function') options.type = options.type.name || firstStringAlias || 'anonymous'; | ||
return options; | ||
}; | ||
Schema.prototype.validate = function(value, prefix) { | ||
@@ -209,2 +253,11 @@ const o = this.error(value, prefix); | ||
return factory; | ||
} | ||
function getNormalizedSchemaConfiguration(obj) { | ||
return Object.getOwnPropertyNames(obj) | ||
.filter(k => k !== 'Schema') | ||
.reduce((prev, key) => { | ||
prev[key] = obj[key]; | ||
return prev; | ||
}, {}); | ||
} |
@@ -35,4 +35,4 @@ /** | ||
if (hasProperties && !util.isPlainObject(config.properties)) { | ||
const message = util.propertyErrorMessage('properties', config.properties, 'Must be a plain object.'); | ||
if (hasProperties && !util.isValidSchemaConfiguration(config.properties)) { | ||
const message = util.propertyErrorMessage('properties', config.properties, 'Must be a plain object or an array of plain objects.'); | ||
const err = Error(message); | ||
@@ -42,2 +42,11 @@ util.throwWithMeta(err, util.errors.config); | ||
if (config.hasOwnProperty('schema')) { | ||
if (!util.isValidSchemaConfiguration(config.schema)) { | ||
const message = util.propertyErrorMessage('schema', config.schema, 'Must be a plain object or an array of plain objects.'); | ||
const err = Error(message); | ||
util.throwWithMeta(err, util.errors.config); | ||
} | ||
validateSchemaConfiguration('schema', config.schema); | ||
} | ||
Object.defineProperties(object, { | ||
@@ -73,2 +82,12 @@ | ||
writable: false | ||
}, | ||
schema: { | ||
/** | ||
* @property | ||
* @name TypedObject#schema | ||
* @type {object, undefined} | ||
*/ | ||
value: config.schema ? Schema(mergeSchemas(config.schema)) : undefined, | ||
writable: false | ||
} | ||
@@ -81,5 +100,7 @@ | ||
.forEach(function(key) { | ||
const value = object.properties[key] || {}; | ||
let options = object.properties[key] || {}; | ||
const optionsIsPlain = !Array.isArray(options); | ||
const schemaIsPlain = !Array.isArray(config.schema); | ||
if (!util.isPlainObject(value)) { | ||
if (!util.isValidSchemaConfiguration(options)) { | ||
const err = Error('Invalid configuration for property: ' + key + '. Must be a plain object.'); | ||
@@ -89,25 +110,35 @@ util.throwWithMeta(err, util.errors.config); | ||
const schema = Schema(value); | ||
// merge generic schema with property specific schemas | ||
if (config.schema) { | ||
if (schemaIsPlain && optionsIsPlain) { | ||
options = mergeSchemas(config.schema, options); | ||
} else if (schemaIsPlain) { | ||
options = options.map(item => mergeSchemas(config.schema, item)); | ||
} else if (optionsIsPlain) { | ||
options = config.schema.map(item => mergeSchemas(item, options)); | ||
} else { | ||
const array = []; | ||
for (let i = 0; i < options.length; i++) { | ||
for (let j = 0; j < config.schema.length; j++) { | ||
array.push(mergeSchemas(config.schema[j], options[i])); | ||
} | ||
} | ||
options = array; | ||
} | ||
} else if (optionsIsPlain) { | ||
options = mergeSchemas(options); | ||
} else { | ||
options = options.map(o => mergeSchemas(o)); | ||
} | ||
// create a schema instance for each property | ||
const schema = Schema(options); | ||
object.properties[key] = schema; | ||
// required | ||
if (value.required && schema.hasDefault) { | ||
const err = Error('Invalid configuration for property: ' + key + '. Cannot make required and provide a default value.'); | ||
util.throwWithMeta(err, util.errors.config); | ||
if (Array.isArray(options)) { | ||
schema.schemas.forEach((s, i) => validateSchemaConfiguration(key, s)) | ||
} else { | ||
validateSchemaConfiguration(key, schema); | ||
} | ||
// add properties to the schema | ||
Object.defineProperties(schema, { | ||
required: { | ||
/** | ||
* @property | ||
* @name Typed#required | ||
* @type {boolean} | ||
*/ | ||
value: value ? !!value.required : false, | ||
writable: false | ||
} | ||
}); | ||
}); | ||
@@ -130,7 +161,7 @@ | ||
const object = this; | ||
// check that all required properties exist | ||
Object.keys(object.properties) | ||
.forEach(function(key) { | ||
const schema = object.properties[key]; | ||
// if required then check that it exists on the value | ||
if (schema.required && !value.hasOwnProperty(key)) { | ||
@@ -140,12 +171,18 @@ const err = util.errish('Missing required value for property: ' + key, TypedObject.errors.required); | ||
errors.push(err); | ||
return; | ||
} | ||
}); | ||
// run inherited error check if it exists on the value | ||
if (value.hasOwnProperty(key)) { | ||
const err = schema.error(value[key]); | ||
if (err) { | ||
err.property = key; | ||
errors.push(err); | ||
} | ||
// validate each property value | ||
Object.keys(value) | ||
.forEach(key => { | ||
const schema = object.properties.hasOwnProperty(key) | ||
? object.properties[key] | ||
: object.schema; | ||
if (!schema) return; | ||
// run inherited error check on property | ||
const err = schema.error(value[key]); | ||
if (err) { | ||
err.property = key; | ||
errors.push(err); | ||
} | ||
@@ -204,2 +241,32 @@ }); | ||
} | ||
}; | ||
}; | ||
function mergeSchemas(general, specific) { | ||
const merged = Object.assign({}, general, specific || {}); | ||
merged.__ = { | ||
properties: { | ||
required: { | ||
value: !!merged.required | ||
} | ||
}/*, | ||
error: function(value, prefix) { | ||
return; | ||
}, | ||
normalize: function(value) { | ||
}*/ | ||
}; | ||
return merged; | ||
} | ||
function validateSchemaConfiguration (key, schema) { | ||
// required | ||
if (schema.required && schema.hasDefault) { | ||
const err = Error('Invalid configuration for property: ' + key + '. Cannot make required and provide a default value.'); | ||
util.throwWithMeta(err, util.errors.config); | ||
} | ||
} |
@@ -18,2 +18,3 @@ /** | ||
'use strict'; | ||
const crypto = require('crypto'); | ||
const util = require('./util'); | ||
@@ -25,3 +26,3 @@ | ||
* Get a typed schema. | ||
* @param {object} [configuration={}] | ||
* @param {object, object[]} [configuration={}] | ||
* @returns {{ error: Function, normalize: Function, validate: Function }} | ||
@@ -36,26 +37,52 @@ */ | ||
// multiple configuration tries all schemas | ||
const schemas = configuration.map(createSchema); | ||
const hashes = {}; | ||
const schemas = configuration.map((config, i) => createSchema(config)) | ||
.filter(schema => { | ||
const hash = schema.hash(); | ||
if (hashes[hash]) return false; | ||
hashes[hash] = true; | ||
return true; | ||
}); | ||
return { | ||
error: function(value, prefix) { | ||
const data = getPassingSchema(schemas, value); | ||
return data.passing ? null : getMultiError(data.errors, prefix); | ||
}, | ||
// generate the hash | ||
const hash = crypto.createHash('sha256') | ||
.update(schemas.map(schema => schema.hash).join('')) | ||
.digest('hex'); | ||
normalize: function(value) { | ||
const data = getPassingSchema(schemas, value, ''); | ||
if (data.passing) return data.schema.normalize(value); | ||
const meta = getMultiError(data.errors, ''); | ||
const err = Error(meta.message); | ||
util.throwWithMeta(err, meta); | ||
}, | ||
const result = new MultiSchema(); | ||
validate: function(value, prefix) { | ||
const o = this.error(value, prefix); | ||
if (o) { | ||
const err = Error(o.message); | ||
util.throwWithMeta(err, o); | ||
} | ||
result.error = function(value, prefix) { | ||
const data = getPassingSchema(schemas, value); | ||
return data.passing ? null : getMultiError(data.errors, prefix); | ||
}; | ||
result.hash = function() { | ||
return hash; | ||
}; | ||
result.normalize = function(value) { | ||
const data = getPassingSchema(schemas, value, ''); | ||
if (data.passing) return data.schema.normalize(value); | ||
const meta = getMultiError(data.errors, ''); | ||
const err = Error(meta.message); | ||
util.throwWithMeta(err, meta); | ||
}; | ||
Object.defineProperty(result, 'schemas', { | ||
get: () => schemas.slice(0) | ||
}); | ||
result.toJSON = function() { | ||
return schemas.map(schema => schema.toJSON()); | ||
}; | ||
result.validate = function(value, prefix) { | ||
const o = this.error(value, prefix); | ||
if (o) { | ||
const err = Error(o.message); | ||
util.throwWithMeta(err, o); | ||
} | ||
} | ||
}; | ||
return result; | ||
} | ||
@@ -120,2 +147,4 @@ | ||
return err; | ||
} | ||
} | ||
function MultiSchema() {} |
@@ -98,2 +98,14 @@ /** | ||
exports.isValidSchemaConfiguration = function(value) { | ||
if (Array.isArray(value)) { | ||
const length = value.length; | ||
for (let i = 0; i < length; i++) { | ||
if (!exports.isPlainObject(value[i])) return false; | ||
} | ||
return true; | ||
} else { | ||
return exports.isPlainObject(value); | ||
} | ||
}; | ||
exports.propertyErrorMessage = function (property, actual, expected) { | ||
@@ -100,0 +112,0 @@ return 'Invalid configuration value for property: ' + property + '. ' + expected + ' Received: ' + quoteWrap(actual); |
{ | ||
"name": "fully-typed", | ||
"version": "1.0.0", | ||
"version": "1.1.0", | ||
"description": "Run time type validation, transformation, and error generator that works out of the box on primitives, objects, arrays, and nested objects. Also extensible for custom types.", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
238
README.md
@@ -1,2 +0,1 @@ | ||
[![npm module downloads](http://img.shields.io/npm/dt/fully-typed.svg)](https://www.npmjs.org/package/fully-typed) | ||
[![Build status](https://img.shields.io/travis/byu-oit-appdev/fully-typed.svg?style=flat)](https://travis-ci.org/byu-oit-appdev/fully-typed) | ||
@@ -11,3 +10,3 @@ | ||
- Create schemas to validate values against. | ||
- Build in support for arrays, booleans, functions, numbers, objects, strings, and symbols. | ||
- Built in support for arrays, booleans, functions, numbers, objects, strings, and symbols. | ||
- Extensible - use plugins or create your own to integrate more types. | ||
@@ -22,2 +21,52 @@ - Get detailed error messages when a wrong value is run against a schema. | ||
const schema = Typed({ | ||
type: Number, | ||
default: 0 | ||
}); | ||
// will only add numbers, throws errors otherwise | ||
exports.add = function (a, b) { | ||
a = schema.normalize(a); // throw an error if not a number or if undefined defaults to 0 | ||
b = schema.normalize(b); | ||
return a + b; | ||
}; | ||
``` | ||
**Example** | ||
```js | ||
const Typed = require('fully-typed'); | ||
const schema = Typed({ | ||
type: Object, | ||
properties: { | ||
name: { | ||
required: true, | ||
type: String, | ||
minLength: 1 | ||
}, | ||
age: { | ||
type: Number, | ||
min: 0 | ||
}, | ||
employed: { | ||
type: Boolean, | ||
default: true | ||
} | ||
} | ||
}); | ||
function addPerson(configuration) { | ||
const config = schema.normalize(configuration); // If the input is invalid an error is thrown | ||
// with specifics as to why it failed | ||
// ... do more stuff | ||
} | ||
``` | ||
**Example** | ||
```js | ||
const Typed = require('fully-typed'); | ||
// define a schema | ||
@@ -299,8 +348,8 @@ const positiveIntegerSchema = Typed({ | ||
type: Number, | ||
max: 1 | ||
min: 1 | ||
}); | ||
schema.error(-1); // no errors | ||
schema.error(-1); // error | ||
schema.error(1); // no errors | ||
schema.error(2); // error | ||
schema.error(2); // no errors | ||
``` | ||
@@ -386,2 +435,82 @@ | ||
- *schema* - (Object) A configuration schema to apply to each property on the object. This is useful for allowing objects to have any property but requiring that each property adhere to a schema. If specific properties are defined then the schema defined here will be extended by and superseded by the specific property's schema. | ||
```js | ||
const schema = Typed({ | ||
type: Object, | ||
// schema specifics for a single property extend the general schema | ||
properties: { | ||
name: { | ||
// the type is inherited as String | ||
minLength: 1, // min length of 1 overwrites general min length of 10 | ||
required: true // name is required | ||
}, | ||
age: { | ||
// because this property is of type Number the non-number properties | ||
// in the general schema definition are ignored | ||
type: Number | ||
} | ||
}, | ||
// a generic schema to apply to all properties within the object | ||
schema: { | ||
type: String, | ||
minLength: 10 | ||
} | ||
}); | ||
schema.error({ name: 'Bob' }); // no errors | ||
schema.error({}); // error | ||
``` | ||
The following example shows a [one-of](#one-of) general schema definition. All variations of the general schema are possible extensions across the property specific schemas. | ||
```js | ||
const schema = Typed({ | ||
type: Object, | ||
// schema specifics for a single property extend the general schema | ||
properties: { | ||
name: { | ||
type: String, // because this is a string it will extend | ||
// the String specific general schema | ||
minLength: 1 | ||
}, | ||
age: { | ||
// because this property is of type Number it extends one of the generic number schemas | ||
type: Number | ||
} | ||
foo: { | ||
// the type might be a String or Number | ||
min: 5, // this property will only apply if the type is a number and | ||
// it will supersede the general min value | ||
minLength: 1, // this property will only apply if the type is a string | ||
required: true // name is required | ||
} | ||
}, | ||
// a generic schema to apply to all properties within the object | ||
schema: [ | ||
{ | ||
type: String, | ||
maxLength: 10 | ||
}, | ||
{ | ||
type: Number, | ||
min: 0, | ||
max: 10 | ||
}, | ||
{ | ||
type: Number, | ||
min: 20, | ||
max: 30 | ||
} | ||
] | ||
}); | ||
schema.error({ name: 'Bob' }); // no errors | ||
schema.error({}); // error | ||
``` | ||
### One-Of | ||
@@ -542,2 +671,101 @@ | ||
schema.validate('a'); // throws an error | ||
``` | ||
## Plugins | ||
### Write the Plugin | ||
To write a plugin you need to define and export the controller for a type. | ||
**truthy-controller.js** | ||
```js | ||
module.exports = Truthy; | ||
function Truthy (config) { | ||
// process the user's schema configuration | ||
const additonalNotTruthyValues = Array.isArray(config.notTruthy) | ||
? config.notTruthy | ||
: []; | ||
const allNotTruthyValues = [false, 0, null, '', undefined].concat(additionalNotTruthyValues); | ||
// define properties that the Foo type keeps | ||
Object.defineProperties(this, { | ||
notTruthy: { | ||
value: allNotTruthyValues, | ||
writable: false | ||
} | ||
}); | ||
} | ||
Foo.prototype.error = function (value, prefix) { | ||
const falsy = this.notTruthy.indexOf(value) !== -1; | ||
return falsy | ||
? prefix + 'Value is not truthy: ' + value | ||
: null; | ||
}; | ||
TypedBoolean.prototype.normalize = function (value) { | ||
return !!value; // make the value true (not just truthy) | ||
}; | ||
``` | ||
### Add a Plugin | ||
You can add an existing plugin to any project by telling fully-typed about the new type controller. | ||
```js | ||
const Typed = require('fully-typed'); | ||
const Truthy = require('./truthy-controller'); | ||
Typed.controllers.define(['truthy'], Truthy, ['typed']); | ||
``` | ||
#### Schema.controllers.define | ||
**Parameters:** | ||
- *aliases* - An array of aliases that are used when defining schemas to identify the controller to use. Schemas can be any value and any type. For example, the predefined `String` schema has two aliases: `['string', String]'`. | ||
- *controller* - The controller to define. | ||
- *inherits* - An array of aliases whose configuration properties, validations, and normalizations should be inherited for this controller. For example, all type definitions for fully-typed inherit from `'typed'`. | ||
#### An Idea | ||
You may not want to ask the user's of your plugin to specify it's aliases and dependencies, but you do need to ask the user of your plugin to supply the Schema library to be used. Here's an alternative. | ||
Your module: | ||
```js | ||
const Truthy = require('./truthy-controller'); | ||
module.exports = function (Typed) { | ||
Typed.controllers.define(['truthy', Truthy], Truthy, ['typed']); | ||
return Truthy; | ||
} | ||
``` | ||
Their Code: | ||
```js | ||
const Typed = require('fully-typed'); | ||
const Truthy = require('truthy')(Typed); | ||
const schema = Typed({ | ||
type: Truthy, // or 'truthy' since that is an alias too | ||
notTruthy: ['false'] // string of 'false' added as non-truthy value | ||
}); | ||
function foo(param) { | ||
schema.validate(param); | ||
// do stuff | ||
}; | ||
foo('hello'); // runs successfully | ||
foo('false'); // throws an error stating: 'Value is not truthy: false' | ||
``` |
@@ -28,7 +28,24 @@ /** | ||
it('properties must be plain object', () => { | ||
const e = util.extractError(() => Schema({ type: Object, properties: null })); | ||
it('properties cannot be null', () => { | ||
const properties = null; | ||
const e = util.extractError(() => Schema({ type: Object, properties: properties })); | ||
expect(e.code).to.equal(util.errors.config.code); | ||
}); | ||
it('properties can be a plain object', () => { | ||
const properties = {}; | ||
expect(() => Schema({ type: Object, properties: properties })).not.to.throw(Error); | ||
}); | ||
it('properties can be an array of plain objects', () => { | ||
const properties = [{}]; | ||
expect(() => Schema({ type: Object, properties: properties })).not.to.throw(Error); | ||
}); | ||
it('properties cannot be an array with a non plain objects', () => { | ||
const properties = [null]; | ||
const e = util.extractError(() => Schema({ type: Object, properties: properties })); | ||
expect(e.code).to.equal(util.errors.config.code); | ||
}); | ||
it('property cannot be number', () => { | ||
@@ -103,2 +120,235 @@ const e = util.extractError(() => Schema({ type: Object, properties: { a: 123 } })); | ||
describe('general schema', () => { | ||
it('can have schema property', () => { | ||
const o = Schema({ type: Object, schema: { type: String } }); | ||
expect(o).to.have.ownProperty('schema'); | ||
}); | ||
it('cannot be a null object', () => { | ||
const config = { type: Object, schema: null }; | ||
expect(() => Schema(config)).to.throw(Error); | ||
}); | ||
it('can be a non null object', () => { | ||
const config = { type: Object, schema: { type: String } }; | ||
expect(() => Schema(config)).to.not.throw(Error); | ||
}); | ||
it('can be an array of objects', () => { | ||
const config = { type: Object, schema: [{ type: String }, { type: Number }] }; | ||
expect(() => Schema(config)).to.not.throw(Error); | ||
}); | ||
describe('extends across properties', () => { | ||
let o; | ||
describe('plain specific and plain general', () => { | ||
before(() => { | ||
o = Schema({ | ||
type: Object, | ||
properties: { | ||
a: { | ||
type: String | ||
}, | ||
b: { | ||
type: Number | ||
}, | ||
c: { | ||
required: false | ||
} | ||
}, | ||
schema: { | ||
type: Boolean, | ||
minLength: 10, | ||
min: 10, | ||
required: true | ||
} | ||
}); | ||
}); | ||
it('a', () => { | ||
expect(o.properties.a.type).to.equal(String); | ||
expect(o.properties.a.minLength).to.equal(10); | ||
expect(o.properties.a).not.to.have.ownProperty('min'); | ||
expect(o.properties.a.required).to.be.true; | ||
}); | ||
it('b', () => { | ||
expect(o.properties.b.type).to.equal(Number); | ||
expect(o.properties.b.min).to.equal(10); | ||
expect(o.properties.b).not.to.have.ownProperty('minLength'); | ||
expect(o.properties.b.required).to.be.true; | ||
}); | ||
it('c', () => { | ||
expect(o.properties.c.type).to.equal(Boolean); | ||
expect(o.properties.c).not.to.have.ownProperty('min'); | ||
expect(o.properties.c).not.to.have.ownProperty('minLength'); | ||
expect(o.properties.c.required).to.be.false; | ||
}); | ||
}); | ||
describe('plain specific and array general', () => { | ||
let o; | ||
before(() => { | ||
o = Schema({ | ||
type: Object, | ||
properties: { | ||
a: { | ||
type: String | ||
}, | ||
b: { | ||
type: Number | ||
}, | ||
c: { | ||
required: false | ||
} | ||
}, | ||
schema: [{ | ||
type: Boolean, | ||
required: true | ||
}] | ||
}); | ||
}); | ||
it('a', () => { | ||
expect(o.properties.a.schemas[0].type).to.equal(String); | ||
expect(o.properties.a.schemas[0].required).to.be.true; | ||
expect(o.properties.a.schemas.length).to.equal(1); | ||
}); | ||
it('b', () => { | ||
expect(o.properties.b.schemas[0].type).to.equal(Number); | ||
expect(o.properties.b.schemas[0].required).to.be.true; | ||
expect(o.properties.b.schemas.length).to.equal(1); | ||
}); | ||
it('c', () => { | ||
expect(o.properties.c.schemas[0].type).to.equal(Boolean); | ||
expect(o.properties.c.schemas[0].required).to.be.false; | ||
expect(o.properties.c.schemas.length).to.equal(1); | ||
}); | ||
}); | ||
describe('array specific and plain general', () => { | ||
let o; | ||
before(() => { | ||
o = Schema({ | ||
type: Object, | ||
properties: { | ||
a: [ | ||
{ | ||
type: String | ||
}, | ||
{ | ||
type: Number, | ||
required: false | ||
} | ||
] | ||
}, | ||
schema: { | ||
type: Boolean, | ||
required: true | ||
} | ||
}); | ||
}); | ||
it('has 2 distinct configurations', () => { | ||
expect(o.properties.a.schemas.length).to.equal(2); | ||
}); | ||
it('a[0]', () => { | ||
expect(o.properties.a.schemas[0].type).to.equal(String); | ||
expect(o.properties.a.schemas[0].required).to.be.true; | ||
}); | ||
it('a[1]', () => { | ||
expect(o.properties.a.schemas[1].type).to.equal(Number); | ||
expect(o.properties.a.schemas[1].required).to.be.false; | ||
}); | ||
}); | ||
describe('array specific and array general', () => { | ||
let o; | ||
before(() => { | ||
o = Schema({ | ||
type: Object, | ||
properties: { | ||
a: [ | ||
{ | ||
type: String | ||
}, | ||
{ | ||
type: Number | ||
} | ||
] | ||
}, | ||
schema: [ | ||
{ | ||
type: String, | ||
required: true | ||
}, | ||
{ | ||
type: Number | ||
}, | ||
{ | ||
min: 0 | ||
} | ||
] | ||
}); | ||
}); | ||
// some combinations create the same configuration, there are 5 distinct configurations here | ||
it('has 5 distinct configurations', () => { | ||
expect(o.properties.a.schemas.length).to.equal(5); | ||
}); | ||
it('a[0]', () => { | ||
const s = o.properties.a.schemas[0]; | ||
expect(s.type).to.equal(String); | ||
expect(s.required).to.be.true; | ||
}); | ||
it('a[1]', () => { | ||
const s = o.properties.a.schemas[1]; | ||
expect(s.type).to.equal(String); | ||
expect(s.required).to.be.false; | ||
}); | ||
it('a[2]', () => { | ||
const s = o.properties.a.schemas[2]; | ||
expect(s.type).to.equal(Number); | ||
expect(s.required).to.be.true; | ||
expect(s.min).to.be.NaN; | ||
}); | ||
it('a[3]', () => { | ||
const s = o.properties.a.schemas[3]; | ||
expect(s.type).to.equal(Number); | ||
expect(s.required).to.be.false; | ||
expect(s.min).to.be.NaN; | ||
}); | ||
it('a[4]', () => { | ||
const s = o.properties.a.schemas[4]; | ||
expect(s.type).to.equal(Number); | ||
expect(s.required).to.be.false; | ||
expect(s.min).to.equal(0); | ||
}); | ||
}); | ||
}); | ||
}); | ||
describe('#error', () => { | ||
@@ -140,2 +390,16 @@ | ||
it('checks for array of schemas', () => { | ||
const o = Schema({ type: Object, properties: { x: [{ type: Number }, {type: String}] } }); | ||
expect(o.error({ x: 'hello' })).to.be.null; | ||
expect(o.error({ x: 123 })).to.be.null; | ||
expect(o.error({ x: true })).not.to.be.null; | ||
}); | ||
it('can validate against non-defined parameters using schema', () => { | ||
const o = Schema({ type: Object, schema: {type: Number} }); | ||
expect(o.error({ foo: 123 })).to.be.null; | ||
expect(o.error({ foo: 'abc' })).not.to.be.null; | ||
expect(o.error({ foo: true })).not.to.be.null; | ||
}); | ||
}); | ||
@@ -142,0 +406,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
329509
39
2672
766