Skip to content

Commit

Permalink
feat: support default case for switch transformer
Browse files Browse the repository at this point in the history
  • Loading branch information
3cp committed May 17, 2018
1 parent 0b3c500 commit fa7d579
Show file tree
Hide file tree
Showing 2 changed files with 203 additions and 3 deletions.
15 changes: 12 additions & 3 deletions src/standard-validators.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,23 +37,26 @@ export const ifTransformer = function (rule) {
export const switchTester = function (rule) {
if (!_.has(rule, 'switch')) return false;
if (!_.has(rule, 'cases')) return false;
if (!_.isEmpty(_.omit(rule, ['switch', 'cases']))) return false;
if (!_.isEmpty(_.omit(rule, ['switch', 'cases', 'default']))) return false;
return (_.isString(rule.switch) || _.isFunction(rule.switch)) &&
_.isObjectLike(rule.cases);
};

export const switchTransformer = function (rule, validate, inPropertyName) {
const _switch = _.get(rule, 'switch');
const cases = _.get(rule, 'cases');
const _default = _.get(rule, 'default');
const switchEvaluator = valueEvaluator(_switch);

const precompiledPlain = _.mapValues(cases, rules => validate(rules));
const precompiledNested = _.mapValues(cases, rules => validate(rules, inPropertyName));
const precompiledPlainDefault = _default && validate(_default);
const precompiledNestedDefault = _default && validate(_default, inPropertyName);

const validator = scope => {
// make a guess whether user try to use nested validation or plain validation
const value = scope.overrideContext.$value;
let precompiled;
let precompiled, precompiledDefault;

if (_.isObjectLike(value)) {
// in nested object
Expand All @@ -66,12 +69,18 @@ export const switchTransformer = function (rule, validate, inPropertyName) {
};

precompiled = precompiledNested[switchEvaluator(newScope)];
precompiledDefault = precompiledNestedDefault;
} else {
// normal switch
precompiled = precompiledPlain[switchEvaluator(scope)];
precompiledDefault = precompiledPlainDefault;
}

return precompiled && precompiled(scope);
if (precompiled) {
return precompiled(scope);
} else if (precompiledDefault) {
return precompiledDefault(scope);
}
};

validator.readyToUse = true;
Expand Down
191 changes: 191 additions & 0 deletions test/standard-transformers-and-validators/switch.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,37 @@ test('switch: switch cases with expression', t => {
t.end();
});

test('switch: switch cases with expression, with default', t => {
let rule = {
value: {
"switch": "type",
"cases": {
"string": {validate: "string", minLength: 4},
"number": ["notMandatory", {validate: "number", min: 10}]
},
"default": "mandatory"
}
};

t.deepEqual(v.validate({value: 'on', type: 'string'}, rule), {
value: ["must have at least 4 characters"]
});

t.deepEqual(v.validate({value: 5, type: 'number'}, rule), {
value: ["must be at least 10"]
});

t.equal(v.validate({value: null, type: 'number'}, rule), undefined);

t.deepEqual(v.validate({value: null, type: 'unknown'}, rule), {
value: ["must not be empty"]
});

t.equal(v.validate({value: /\d/, type: 'unknown'}, rule), undefined);

t.end();
});

test('switch: switch cases with func', t => {
let rule = {
value: {
Expand All @@ -50,6 +81,37 @@ test('switch: switch cases with func', t => {
t.end();
});

test('switch: switch cases with func, with default', t => {
let rule = {
value: {
"switch": (v, path, obj) => obj.type,
"cases": {
"string": {validate: "string", minLength: 4},
"number": ["notMandatory", {validate: "number", min: 10}]
},
"default": "mandatory"
}
};

t.deepEqual(v.validate({value: 'on', type: 'string'}, rule), {
value: ["must have at least 4 characters"]
});

t.deepEqual(v.validate({value: 5, type: 'number'}, rule), {
value: ["must be at least 10"]
});

t.equal(v.validate({value: null, type: 'number'}, rule), undefined);

t.deepEqual(v.validate({value: null, type: 'unknown'}, rule), {
value: ["must not be empty"]
});

t.equal(v.validate({value: /\d/, type: 'unknown'}, rule), undefined);

t.end();
});

test('switch: switch cases on nested validation', t => {
let rule = {
meta: {
Expand All @@ -73,6 +135,37 @@ test('switch: switch cases on nested validation', t => {
t.end();
});

test('switch: switch cases on nested validation, with default', t => {
let rule = {
meta: {
"switch": "type",
"cases": {
"string": {value: {validate: "string", minLength: 4}},
"number": {value: ["notMandatory", {validate: "number", min: 10}]}
},
"default": {value: "mandatory"}
}
};

t.deepEqual(v.validate({meta: {value: 'on', type: 'string'}}, rule), {
meta: {value: ["must have at least 4 characters"]}
});

t.deepEqual(v.validate({meta: {value: 5, type: 'number'}}, rule), {
meta: {value: ["must be at least 10"]}
});

t.equal(v.validate({meta: {value: null, type: 'number'}}, rule), undefined);

t.deepEqual(v.validate({meta: {value: null, type: 'unknown'}}, rule), {
meta: {value: ["must not be empty"]}
});

t.equal(v.validate({meta: {value: /\d/, type: 'unknown'}}, rule), undefined);

t.end();
});

test('switch: complex switch cases on nested validation', t => {
let rule = {
meta: {
Expand Down Expand Up @@ -103,6 +196,39 @@ test('switch: complex switch cases on nested validation', t => {
t.end();
});

test('switch: complex switch cases on nested validation, with default', t => {
let rule = {
meta: {
"switch": "domain + ':' + type",
"cases": {
"admin:string": {value: {validate: "string", minLength: 4}},
"admin:number": {value: ["notMandatory", {validate: "number", min: 10}]}
},
"default": {value: "mandatory"}
}
};

t.deepEqual(v.validate({meta: {value: 'on', type: 'string', domain: 'admin'}}, rule), {
meta: {value: ["must have at least 4 characters"]}
});

t.deepEqual(v.validate({meta: {value: 5, type: 'number', domain: 'admin'}}, rule), {
meta: {value: ["must be at least 10"]}
});

t.equal(v.validate({meta: {value: null, type: 'number', domain: 'admin'}}, rule), undefined);

t.equal(v.validate({meta: {value: 'on', type: 'string', domain: 'user'}}, rule), undefined);

t.equal(v.validate({meta: {value: 5, type: 'number', domain: 'user'}}, rule), undefined);

t.deepEqual(v.validate({meta: {value: null, type: 'number', domain: 'user'}}, rule), {
meta: {value: ["must not be empty"]}
});

t.end();
});

test('switch: func switch on nested validation', t => {
let rule = {
meta: {
Expand Down Expand Up @@ -133,6 +259,39 @@ test('switch: func switch on nested validation', t => {
t.end();
});

test('switch: func switch on nested validation, with default', t => {
let rule = {
meta: {
"switch": o => o.domain + ':' + o.type,
"cases": {
"admin:string": {value: {validate: "string", minLength: 4}},
"admin:number": {value: ["notMandatory", {validate: "number", min: 10}]}
},
"default": {value: "mandatory"}
}
};

t.deepEqual(v.validate({meta: {value: 'on', type: 'string', domain: 'admin'}}, rule), {
meta: {value: ["must have at least 4 characters"]}
});

t.deepEqual(v.validate({meta: {value: 5, type: 'number', domain: 'admin'}}, rule), {
meta: {value: ["must be at least 10"]}
});

t.equal(v.validate({meta: {value: null, type: 'number', domain: 'admin'}}, rule), undefined);

t.equal(v.validate({meta: {value: 'on', type: 'string', domain: 'user'}}, rule), undefined);

t.equal(v.validate({meta: {value: 5, type: 'number', domain: 'user'}}, rule), undefined);

t.deepEqual(v.validate({meta: {value: null, type: 'number', domain: 'user'}}, rule), {
meta: {value: ["must not be empty"]}
});

t.end();
});

test('switch: switch cases after if', t => {
let rule = {
value: {
Expand All @@ -158,6 +317,38 @@ test('switch: switch cases after if', t => {
t.end();
});

test('switch: switch cases after if, with default', t => {
let rule = {
value: {
"if": "enforce",
"switch": "type",
"cases": {
"string": {validate: "string", minLength: 4},
"number": {validate: "number", min: 10}
},
"default": "mandatory"
}
};

t.equal(v.validate({value: 'on', type: 'string', enforce: false}, rule), undefined);
t.deepEqual(v.validate({value: 'on', type: 'string', enforce: true}, rule), {
value: ["must have at least 4 characters"]
});

t.equal(v.validate({value: 5, type: 'number', enforce: false}, rule), undefined);
t.deepEqual(v.validate({value: 5, type: 'number', enforce: true}, rule), {
value: ["must be at least 10"]
});

t.deepEqual(v.validate({value: null, type: 'unknown', enforce: true}, rule), {
value: ["must not be empty"]
});

t.equal(v.validate({value: null, type: 'unknown', enforce: false}, rule), undefined);

t.end();
});

test('switch: smart enough to separate validation rule from switch transformer', t => {
t.deepEqual(v.validate({meta: {switch: ''}}, {meta: {switch: 'mandatory'}}), {meta: {switch: ["must not be empty"]}});
t.deepEqual(v.validate({meta: {switch: '', cases: ''}}, {meta: {switch: 'mandatory', cases: 'mandatory'}}),
Expand Down

0 comments on commit fa7d579

Please sign in to comment.