Commit 356bbf30 authored by Stefan Cameron's avatar Stefan Cameron

Adding JSON type

This is the last of all the types!

Coverage: 100%
parent 28292b56
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -1533,13 +1533,13 @@ Weak set rules per qualifiers: Must be a `WeakSet` instance.
#### types.JSON : <code>string</code>
JSON rules per qualifiers: Must be a JSON value:
- [null](#rtvref.types.NULL)
- [string](#rtvref.types.STRING), however __empty strings are permitted__,
even if the qualifier is `REQUIRED`;
- [boolean](#rtvref.types.BOOLEAN);
- [finite number](#rtvref.types.FINITE);
- [plain object](#rtvref.types.PLAIN_OBJECT);
- [array](#rtvref.types.ARRAY);
- `null`
even if the qualifier is `REQUIRED`
- [boolean](#rtvref.types.BOOLEAN)
- [finite number](#rtvref.types.FINITE)
- [plain object](#rtvref.types.PLAIN_OBJECT)
- [array](#rtvref.types.ARRAY)
Since this type checks for _any_ valid JSON value, empty string and `null`
values are permitted, even when the typeset is qualified as `REQUIRED`.
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -1067,13 +1067,13 @@ const defs = {
/**
* JSON rules per qualifiers: Must be a JSON value:
*
* - {@link rtvref.types.NULL null}
* - {@link rtvref.types.STRING string}, however __empty strings are permitted__,
* even if the qualifier is `REQUIRED`;
* - {@link rtvref.types.BOOLEAN boolean};
* - {@link rtvref.types.FINITE finite number};
* - {@link rtvref.types.PLAIN_OBJECT plain object};
* - {@link rtvref.types.ARRAY array};
* - `null`
* even if the qualifier is `REQUIRED`
* - {@link rtvref.types.BOOLEAN boolean}
* - {@link rtvref.types.FINITE finite number}
* - {@link rtvref.types.PLAIN_OBJECT plain object}
* - {@link rtvref.types.ARRAY array}
*
* Since this type checks for _any_ valid JSON value, empty string and `null`
* values are permitted, even when the typeset is qualified as `REQUIRED`.
......
////// isJson validation
import isNull from './isNull';
import isString from './isString';
import isBoolean from './isBoolean';
import isFinite from './isFinite';
import isPlainObject from './isPlainObject';
import isArray from './isArray';
import types from '../types';
/**
* Type: {@link rtvref.types.JSON JSON}
* @const {string} rtvref.validation.isJson.type
*/
export const type = types.JSON;
/**
* {@link rtvref.validation.method Validation} for the
* {@link rtvref.types.JSON JSON} type.
* @function rtvref.validation.isJson.default
* @param {*} v Value to validate.
* @returns {boolean} `true` if validated; `false` otherwise.
*/
export default function isJson(v) {
return isNull(v) ||
isString(v, {allowEmpty: true}) ||
isBoolean(v) ||
isFinite(v) ||
isPlainObject(v) ||
isArray(v);
}
......@@ -68,11 +68,11 @@ export default function valClassObject(v, q = REQUIRED, args) {
// only consider enumerable, own-properties of the shape
_forEach(shape, function(typeset, prop) {
const result = impl.check(v[prop], typeset); // check prop value against shape prop typeset
const propResult = impl.check(v[prop], typeset); // check prop value against shape prop typeset
if (!result.valid) {
if (!propResult.valid) {
err = new RtvError(v, impl.toTypeset(type, q, args),
[prop].concat(result.path), result.cause);
[prop].concat(propResult.path), propResult.cause);
}
return !err; // break on first error
......
////// valJson validator
import {type, default as isJson} from '../validation/isJson';
import {default as qualifiers, nilPermitted} from '../qualifiers';
import RtvSuccess from '../RtvSuccess';
import RtvError from '../RtvError';
const {REQUIRED} = qualifiers;
let impl; // @type {rtvref.impl}
/**
* [Internal] __FOR UNIT TESTING ONLY:__ The {@link rtvref.impl} instance
* configured on this validator.
* @private
* @name rtvref.validator.valJson._impl
* @type {rtvref.impl}
*/
export {impl as _impl};
/**
* Type: {@link rtvref.types.JSON JSON}
* @const {string} rtvref.validator.valJson.type
*/
export {type};
/**
* {@link rtvref.validator.validator_config Configuration Function}
* @function rtvref.validator.valJson.config
* @param {rtvref.validator.validator_config_settings} settings Configuration settings.
*/
export const config = function(settings) {
impl = settings.impl;
};
/**
* {@link rtvref.validator.type_validator Validator} for the
* {@link rtvref.types.JSON JSON} type.
* @function rtvref.validator.valJson.default
* @param {*} v Value to validate.
* @param {string} [q] Validation qualifier. Defaults to
* {@link rtvref.qualifiers.REQUIRED REQUIRED}.
* @returns {(rtvref.RtvSuccess|rtvref.RtvError)} An `RtvSuccess` if valid; `RtvError` if not.
*/
export default function valJson(v, q = REQUIRED) {
if (nilPermitted(v, q)) {
return new RtvSuccess();
}
if (isJson(v)) {
return new RtvSuccess();
}
return new RtvError(v, impl.toTypeset(type, q), [], impl.toTypeset(type, q, true));
}
......@@ -20,6 +20,7 @@ import * as valFinite from './lib/validator/valFinite';
import * as valFunction from './lib/validator/valFunction';
import * as valHashMap from './lib/validator/valHashMap';
import * as valInt from './lib/validator/valInt';
import * as valJson from './lib/validator/valJson';
import * as valMap from './lib/validator/valMap';
import * as valNull from './lib/validator/valNull';
import * as valNumber from './lib/validator/valNumber';
......@@ -290,6 +291,7 @@ export default rtv;
valFunction,
valHashMap,
valInt,
valJson,
valNull,
valMap,
valNumber,
......
......@@ -22,7 +22,7 @@ describe('module: lib/impl', function() {
value: impl._validatorMap
});
expect(isObject(impl._validatorMap)).to.be.true;
expect(Object.keys(impl._validatorMap).length).to.equal(24); // # of known types
expect(Object.keys(impl._validatorMap).length).to.equal(25); // # of known types
});
});
......
......@@ -43,6 +43,7 @@ describe('module: lib/validation/isClassObject', function() {
new Boolean(false),
new Number(1),
new Object(),
Object.create(null),
{}
]);
......
import {expect} from 'chai';
import * as vtu from '../validationTestUtil';
import types from '../../../src/lib/types';
import * as val from '../../../src/lib/validation/isJson';
describe('module: lib/validation/isJson', function() {
it('#type', function() {
expect(val.type).to.equal(types.JSON);
});
describe('#default', function() {
let jsonValues;
let invalidValues;
beforeEach(function() {
jsonValues = vtu.getJsonValues();
invalidValues = vtu.getInvalidJsonValues();
});
it('valid values', function() {
expect(vtu.testValues(val.type, val.default, jsonValues).failures).to.eql([]);
});
it('other types/values', function() {
// nothing should pass
expect(vtu.testValues(val.type, val.default, invalidValues).passes).to.eql([]);
});
});
});
......@@ -26,9 +26,13 @@ export const getValidValues = function(type) {
// primitives
//
// NOTE: specific values for types.ANY would be [undefined, null], and for types.NULL
// would be [null], but these would cause issues with testOtherValues() tests,
// so we exclude them and special-case them in unit tests
// NOTE: we purposely __omit__ the following types and associated values because
// they would cause a lot of issues with testOtherValues() due to overlap with
// many types:
// - types.ANY: [undefined, null]
// - types.NULL: [null]
// - types.JSON: [null, 'string', '', true, false, 1, [], {}] -- see
// getJsonValues() and getInvalidJsonValues()
[types.STRING]: ['literal-string'],
[types.BOOLEAN]: [true, false],
......@@ -119,7 +123,7 @@ export const getValidValues = function(type) {
[types.FUNCTION]: [function() {}, new Function('a', 'b', 'return a + b;')],
// while the JS type is objects, it's not an object type in this library
[types.HASH_MAP]: [new Object(), {}, new (function() {})()],
[types.HASH_MAP]: [new Object(), {}, new (class {})],
//
// object types
......@@ -131,16 +135,19 @@ export const getValidValues = function(type) {
new Boolean(false),
new Number(1),
new Object(),
Object.create(null),
{},
new (class {})
],
[types.OBJECT]: [
new Object(),
Object.create(null),
{},
new (class {})
],
[types.PLAIN_OBJECT]: [
new Object(),
Object.create(null),
{}
],
[types.CLASS_OBJECT]: [
......@@ -166,6 +173,61 @@ export const getAllValues = function() {
return values;
};
/**
* Get valid JSON values.
* @returns {Array} Valid JSON values for testing.
*/
export const getJsonValues = function() {
return [
null,
'string',
'',
true,
false,
1,
[],
{},
Object.create(null),
new Object()
];
};
/**
* Get invalid JSON values.
* @returns {Array} Invalid JSON values for testing.
*/
export const getInvalidJsonValues = function() {
const validTypeValues = getValidValues();
let invalidValues = [
undefined,
NaN,
Infinity,
Number.POSITIVE_INFINITY,
-Infinity,
Number.NEGATIVE_INFINITY,
new String('new-string'),
new Boolean(true),
new Boolean(false),
new Number(1),
new (class {})
];
invalidValues = invalidValues.concat(
validTypeValues[types.SYMBOL],
validTypeValues[types.MAP],
validTypeValues[types.WEAK_MAP],
validTypeValues[types.SET],
validTypeValues[types.WEAK_SET],
validTypeValues[types.DATE],
validTypeValues[types.FUNCTION],
validTypeValues[types.REGEXP],
validTypeValues[types.ERROR],
validTypeValues[types.PROMISE]);
return invalidValues;
};
/**
* Determines if a test result is a pass.
* @param {(boolean|rtvref.RtvSuccess)} result Test result.
......
......@@ -65,6 +65,7 @@ describe('module: lib/validator/valClassObject', function() {
new Boolean(false),
new Number(1),
new Object(),
Object.create(null),
{}
]);
......
import {expect} from 'chai';
import sinon from 'sinon';
import * as vtu from '../validationTestUtil';
import types from '../../../src/lib/types';
import qualifiers from '../../../src/lib/qualifiers';
import * as val from '../../../src/lib/validator/valJson';
describe('module: lib/validator/valJson', function() {
describe('validator', function() { // module, and value only
let jsonValues;
let invalidValues;
beforeEach(function() {
jsonValues = vtu.getJsonValues();
invalidValues = vtu.getInvalidJsonValues();
});
it('#type', function() {
expect(val.type).to.equal(types.JSON);
});
it('succeeds with an RtvSuccess', function() {
vtu.expectValidatorSuccess(val, {});
});
it('valid values', function() {
expect(vtu.testValues(val.type, val.default, jsonValues).failures).to.eql([]);
});
it('other types/values', function() {
// nothing should pass
expect(vtu.testValues(val.type, val.default, invalidValues).passes).to.eql([]);
});
});
describe('qualifiers', function() {
describe('rules are supported', function() {
it('REQUIRED (other than values previously tested)', function() {
vtu.expectValidatorError(val, undefined, qualifiers.REQUIRED);
vtu.expectValidatorSuccess(val, null, qualifiers.REQUIRED);
vtu.expectValidatorSuccess(val, '', qualifiers.REQUIRED);
});
it('EXPECTED', function() {
vtu.expectValidatorError(val, undefined, qualifiers.EXPECTED);
vtu.expectValidatorSuccess(val, null, qualifiers.EXPECTED);
vtu.expectValidatorSuccess(val, '', qualifiers.EXPECTED);
});
it('OPTIONAL', function() {
vtu.expectValidatorSuccess(val, undefined, qualifiers.OPTIONAL);
vtu.expectValidatorSuccess(val, null, qualifiers.OPTIONAL);
vtu.expectValidatorSuccess(val, '', qualifiers.OPTIONAL);
});
});
describe('are used in error typesets', function() {
it('DEFAULT', function() {
vtu.expectValidatorError(val, Symbol(1)); // default should be REQUIRED
});
it('REQUIRED', function() {
vtu.expectValidatorError(val, Symbol(1), qualifiers.REQUIRED);
});
it('EXPECTED', function() {
vtu.expectValidatorError(val, Symbol(1), qualifiers.EXPECTED);
});
it('OPTIONAL', function() {
vtu.expectValidatorError(val, Symbol(1), qualifiers.OPTIONAL);
});
});
});
});
......@@ -25,6 +25,7 @@ import * as isFinite from '../src/lib/validation/isFinite';
import * as isFunction from '../src/lib/validation/isFunction';
import * as isHashMap from '../src/lib/validation/isHashMap';
import * as isInt from '../src/lib/validation/isInt';
import * as isJson from '../src/lib/validation/isJson';
import * as isMap from '../src/lib/validation/isMap';
import * as isNull from '../src/lib/validation/isNull';
import * as isNumber from '../src/lib/validation/isNumber';
......@@ -51,6 +52,7 @@ import * as valFinite from '../src/lib/validator/valFinite';
import * as valFunction from '../src/lib/validator/valFunction';
import * as valHashMap from '../src/lib/validator/valHashMap';
import * as valInt from '../src/lib/validator/valInt';
import * as valJson from '../src/lib/validator/valJson';
import * as valMap from '../src/lib/validator/valMap';
import * as valNull from '../src/lib/validator/valNull';
import * as valNumber from '../src/lib/validator/valNumber';
......@@ -99,6 +101,7 @@ global.rtvi = {
isFunction,
isHashMap,
isInt,
isJson,
isMap,
isNull,
isNumber,
......@@ -127,6 +130,7 @@ global.rtvi = {
valFunction,
valHashMap,
valInt,
valJson,
valMap,
valNull,
valNumber,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment