Commit aa8e13ed authored by Stefan Cameron's avatar Stefan Cameron

Finished isMap, added isSet

parent 7a11a665
......@@ -4,12 +4,25 @@ import {default as _isMap} from 'lodash/isMap';
import {validator as isFinite} from './isFinite';
import {validator as isString} from './isString';
import {validator as isArray} from './isArray';
import types from '../types';
import qualifiers from '../qualifiers';
import {isTypeset} from './validation';
import * as impl from '../impl';
/**
* Determines if a typeset represents a string, and only a string.
* @param {rtvref.types.typeset} ts Typeset to check.
* @return {boolean} `true` if so; `false` otherwise.
*/
const isStringTypeset = function(ts) {
const fqts = impl.fullyQualify(ts);
// must be `[qualifier, STRING]`, otherwise no
return (fqts.length === 2 && fqts[1] === types.STRING);
};
/**
* {@link rtvref.validation.validator Validator} function for the
* {@link rtvref.types.MAP MAP} type.
......@@ -35,7 +48,8 @@ export const validator = function isMap(v, q = qualifiers.REQUIRED, args) {
// get the typeset for keys
const tsKeys = isTypeset(args.keys) ? args.keys : undefined;
// get the key expression only if the keys are expected to be strings
const keyExp = (tsKeys === types.STRING && isString(args.keyExp)) ?
const tsKeysIsString = isStringTypeset(tsKeys);
const keyExp = (tsKeysIsString && isString(args.keyExp)) ?
args.keyExp : undefined;
// get the key expression flags only if we have a key expression
const keyFlagSpec = (keyExp && isString(args.keyFlagSpec)) ?
......@@ -46,14 +60,13 @@ export const validator = function isMap(v, q = qualifiers.REQUIRED, args) {
if (tsKeys || tsValues) {
const reKeys = keyExp ? new RegExp(keyExp, keyFlagSpec) : undefined;
const it = v.entries(); // iterator
let next = it.next();
while (valid && !next.done) {
const [key, value] = next.value;
for (let elem of it) {
const [key, value] = elem.value;
if (valid && tsKeys) {
if (tsKeys) {
valid = impl.check(key, tsKeys); // check key against typeset
if (valid && tsKeys === types.STRING && reKeys) {
if (valid && tsKeysIsString && reKeys) {
valid = reKeys.test(key); // check key against regex since it's a string
}
}
......@@ -62,7 +75,9 @@ export const validator = function isMap(v, q = qualifiers.REQUIRED, args) {
valid = impl.check(value, tsValues); // check value against typeset
}
next = it.next();
if (!valid) {
break;
}
}
}
}
......
////// isSet validator
import {default as _isSet} from 'lodash/isSet';
import {validator as isFinite} from './isFinite';
import {validator as isString} from './isString';
import {validator as isArray} from './isArray';
import types from '../types';
import qualifiers from '../qualifiers';
import {isTypeset} from './validation';
import * as impl from '../impl';
/**
* Determines if a typeset represents a string, and only a string.
* @param {rtvref.types.typeset} ts Typeset to check.
* @return {boolean} `true` if so; `false` otherwise.
*/
const isStringTypeset = function(ts) {
const fqts = impl.fullyQualify(ts);
// must be `[qualifier, STRING]`, otherwise no
return (fqts.length === 2 && fqts[1] === types.STRING);
};
/**
* {@link rtvref.validation.validator Validator} function for the
* {@link rtvref.types.SET SET} type.
* @function rtvref.validation.isSet
* @param {*} v Value to validate.
* @param {string} [q] Validation qualifier. Defaults to
* {@link rtvref.qualifiers.REQUIRED REQUIRED}.
* @param {rtvref.types.collection_args} [args] Type arguments.
* @returns {boolean} `true` if validated; `false` otherwise.
*/
export const validator = function isSet(v, q = qualifiers.REQUIRED, args) {
let valid = _isSet(v);
if (valid) {
if (valid && args) { // then check args
// start with the easiest/most efficient test: length
if (valid && isFinite(args.length) && args.length >= 0) {
valid = (v.size >= args.length);
}
// remaining args, if specified, require iterating potentially the entire set
if (valid) {
// get the typeset for values
const tsValues = isTypeset(args.values) ? args.values : undefined;
if (tsValues) {
const it = v.entries(); // iterator
for (let elem of it) {
const value = elem.value[1];
valid = impl.check(value, tsValues); // check value against typeset
if (!valid) {
break;
}
}
}
}
}
}
return valid;
};
export const type = types.SET;
......@@ -88,15 +88,6 @@ import qualifiers from '../qualifiers';
* and args; `false` otherwise.
*/
/**
* Determines if a value is a set.
* @function rtvref.validation.isSet
* @param {*} v Value to validate.
* @returns {boolean} `true` if it is; `false` otherwise.
* @see {@link rtvref.types.SET}
*/
export const isSet = _isSet; // DEBUG TODO move to validator after impl isMap
/**
* Determines if a value is a JavaScript {@link rtvref.types.primitives primitive}.
* @function rtvref.validation.isPrimitive
......
import {expect} from 'chai';
import _ from 'lodash';
import * as vtu from './validationTestUtil';
import types from '../../../src/lib/types';
......@@ -39,10 +38,107 @@ describe('module: lib/validation/isMap', function() {
expect(val.validator(map, undefined, {length: Number.NEGATIVE_INFINITY})).to.be.true; // ignored
});
it('checks for keys with specified typeset'); // TODO
it('checks for keys with specified typeset', function() {
const map = new Map([[1, 'one'], [2, 'two'], [3, 'three']]);
expect(val.validator(map, undefined, {
keys: types.FINITE
})).to.be.true;
expect(val.validator(map, undefined, {
keys: types.STRING
})).to.be.false;
expect(val.validator(new Map(), undefined, {
keys: types.REGEXP
})).to.be.true;
});
it('checks for strings keys that match a pattern'); // TODO
it('checks for strings keys that match a pattern', function() {
let map = new Map([1, 'one'], [2, 'two']);
expect(val.validator(map, undefined, {
keys: types.FINITE,
keyExp: 'key' // ignored: keys aren't expected to be strings
})).to.be.true;
map = new Map(['key1', 1], ['key2', 2]);
expect(val.validator(map, undefined, {
keys: types.FINITE
})).to.be.false; // keys are not strings in this map
expect(val.validator(map, undefined, {
keys: types.STRING,
keyExp: 'key\\d'
})).to.be.true;
expect(val.validator(map, undefined, {
keys: types.STRING,
keyExp: function() {} // ignored: not string
})).to.be.true;
expect(val.validator(map, undefined, {
keys: [types.STRING],
keyExp: 'key\\d'
})).to.be.true;
expect(val.validator(map, undefined, {
keys: [qualifiers.EXPECTED, types.STRING],
keyExp: 'key\\d'
})).to.be.true;
expect(val.validator(map, undefined, {
keys: [qualifiers.EXPECTED, types.STRING],
keyExp: 'KEY\\d'
})).to.be.false; // case-sensitive by default
expect(val.validator(map, undefined, {
keys: [qualifiers.EXPECTED, types.STRING],
keyExp: 'KEY\\d',
keyFlagSpec: 'i' // case-insensitive flag
})).to.be.true;
expect(val.validator(map, undefined, {
keys: [qualifiers.EXPECTED, types.STRING],
keyExp: 'KEY\\d',
keyFlagSpec: {} // ignored: not string
})).to.be.false;
});
it('checks for values with specified typeset'); // TODO
it('checks for values with specified typeset', function() {
let map = new Map([[1, 'one'], [2, 'two'], [3, 'three']]);
expect(val.validator(map, undefined, {
values: types.STRING
})).to.be.true;
expect(val.validator(map, undefined, {
values: types.BOOLEAN
})).to.be.false;
map = new Map([[1, 'one'], [2, 'two'], [3, '']]);
expect(val.validator(map, undefined, {
values: types.STRING // required by default, so will fail
})).to.be.false;
expect(val.validator(map, undefined, {
values: [qualifiers.EXPECTED, types.STRING]
})).to.be.true;
});
it('checks for keys and values with specified typeset', function() {
const map = new Map([
[1, new Map(['1', true])],
[2, new Map(['2', false])],
[3, new Map(['3', true])]
]);
expect(val.validator(map, undefined, {
keys: types.FINITE,
values: [types.MAP, {
keys: types.STRING,
values: types.BOOLEAN
}]
})).to.be.true;
expect(val.validator(map, undefined, {
keys: types.FINITE,
values: [types.MAP, {
keys: [types.STRING, {min: 2}],
values: types.BOOLEAN
}]
})).to.be.false; // keys in nested maps are not strings of >= 2 chars
});
});
});
import {expect} from 'chai';
import * as vtu from './validationTestUtil';
import types from '../../../src/lib/types';
import * as val from '../../../src/lib/validation/isSet';
describe('module: lib/validation/isSet', function() {
describe('validator', function() {
it('type', function() {
expect(val.type).to.equal(types.SET);
});
it('valid values', function() {
expect(vtu.testValues(val.type, val.validator).failures).to.eql([]);
});
it('other types/values', function() {
expect(vtu.testOtherValues(val.type, val.validator)).to.eql([]);
});
});
describe('arguments', function() {
it('checks for an exact length', function() {
const set = new Set([1, 2, 3]);
expect(val.validator(new Map(), undefined, {length: 0})).to.be.true;
expect(val.validator(set, undefined, {length: 3})).to.be.true;
expect(val.validator(set, undefined, {length: 2})).to.be.false;
expect(val.validator(set, undefined, {length: 1.1})).to.be.false;
expect(val.validator(set, undefined, {length: '1'})).to.be.true; // ignored
expect(val.validator(set, undefined, {length: -0})).to.be.true; // ignored
expect(val.validator(set, undefined, {length: -1})).to.be.true; // ignored
expect(val.validator(set, undefined, {length: NaN})).to.be.true; // ignored
expect(val.validator(set, undefined, {length: Infinity})).to.be.true; // ignored
expect(val.validator(set, undefined, {length: -Infinity})).to.be.true; // ignored
expect(val.validator(set, undefined, {length: Number.POSITIVE_INFINITY})).to.be.true; // ignored
expect(val.validator(set, undefined, {length: Number.NEGATIVE_INFINITY})).to.be.true; // ignored
});
it('checks for values with specified typeset', function() {
let set = new Set(['one', 'two', 'three']);
expect(val.validator(set, undefined, {
values: types.STRING
})).to.be.true;
expect(val.validator(set, undefined, {
values: types.BOOLEAN
})).to.be.false;
set = new Set(['one', 'two', '']);
expect(val.validator(set, undefined, {
values: types.STRING // required by default, so will fail
})).to.be.false;
expect(val.validator(set, undefined, {
values: [qualifiers.EXPECTED, types.STRING]
})).to.be.true;
set = new Set([
new Set(['1']),
new Set(['2']),
new Set(['3'])
]);
expect(val.validator(set, undefined, {
values: [types.SET, {
keys: types.STRING // ignored: sets don't have keys
}]
})).to.be.true;
expect(val.validator(set, undefined, {
values: [types.SET, {
values: types.STRING
}]
})).to.be.true;
expect(val.validator(set, undefined, {
values: [types.SET, {
length: 2,
values: types.STRING
}]
})).to.be.false; // nested sets do not have length >= 2
});
});
});
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