Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 33 additions & 3 deletions lib/validator.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,15 @@ Validator.prototype._processRule = function(rule, name, iterate) {
throw new Error("Invalid '" + rule.type + "' type in validator schema!");
}


/**
* !IMPORTANT!: For the functioning of multiRule cases it is important that
* pushing of object and array special rules is done in directly after this
* simple rule.
* If you which to push other checks, do it before the simple ones or after
* the array special case.
* See the comments in _checkWrapper for further explanation.
*/
checks.push({
fn: this.rules[rule.type],
type: rule.type,
Expand All @@ -125,7 +134,8 @@ Validator.prototype._processRule = function(rule, name, iterate) {
type: rule.type,
name: name,
schema: rule,
iterate: iterate
iterate: iterate,
secondPart: true //first part is the "primitive" typeof check above
});
}

Expand All @@ -137,7 +147,8 @@ Validator.prototype._processRule = function(rule, name, iterate) {
type: rule.type,
name: name,
schema: rule,
iterate: true
iterate: true,
secondPart: true //first part is the "primitive" typeof check above
});
}

Expand Down Expand Up @@ -181,18 +192,37 @@ Validator.prototype._checkWrapper = function(rules, isMultipleRules) {
} else {
// Call the checker function
if (check.iterate) {
let errorInCurrentArray = false;
const l = value.length;
for (let i = 0; i < l; i++) {
let _stack = stack + "[" + i + "]";
let res = check.fn.call(self, value[i], schema, _stack, obj);
if (res !== true)
if (res !== true) {
errorInCurrentArray = true;
self.handleResult(errors, _stack, res);
}
}
/**
* If this is second part of a multiRule array check and the array
* is valid, then the rule is valid.
*/
if (!errorInCurrentArray && isMultipleRules && check.secondPart) {
return true;
}
} else {
let res = check.fn.call(self, value, schema, stack, obj);

if (isMultipleRules) {
if (res === true) {
/**
* Object and array checks are divided into two internal checks. In case of a multiRule
* we have to make sure to check both parts. Thus we we continue to also do the second
* check if their is one.
*/
const nextRule = rules[i+1]
if (nextRule && nextRule.secondPart){
continue;
}
// Jump out after first success and clear previous errors
return true;
}
Expand Down
263 changes: 263 additions & 0 deletions test/validator.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -631,3 +631,266 @@ describe("Test multiple rules", () => {
});

});

describe("Test multiple rules with objects", () => {
const v = new Validator();

let schema = {
list: [
{
type: "object",
props: {
name: {type: "string"},
age: {type: "number"},
}
},
{
type: "object",
props: {
country: {type: "string"},
code: {type: "string"},
}
}
]
};

let check = v.compile(schema);

it("should give true if first object is given", () => {
let obj = { list: {
name: "Joe",
age: 34
} };

let res = check(obj);

expect(res).toBe(true);
});

it("should give true if second object is given", () => {
let obj = { list: {
country: "germany",
code: "de"
}};

let res = check(obj);

expect(res).toBe(true);
});

it("should give error if the object is broken", () => {
let obj = { list: {
name: "Average",
age: "Joe"
} };

let res = check(obj);

expect(res).toBeInstanceOf(Array);
expect(res.length).toBe(3);
expect(res[0].type).toBe("number");
expect(res[0].field).toBe("list.age");

expect(res[1].type).toBe("required");
expect(res[1].field).toBe("list.country");
});

it("should give error if the object is only partly given", () => {
let obj = { list: {} };
let res = check(obj);

expect(res).toBeInstanceOf(Array);
expect(res.length).toBe(4);
expect(res[0].type).toBe("required");
expect(res[0].field).toBe("list.name");

expect(res[1].type).toBe("required");
expect(res[1].field).toBe("list.age");

});

});

describe("Test multiple rules with objects within array", () => {
const v = new Validator();

let schema = {
list: {
type: "array",
items: [
{
type: "object",
props: {
name: {type: "string"},
age: {type: "number"},
}
},
{
type: "object",
props: {
country: {type: "string"},
code: {type: "string"},
}
}
]
}
};

let check = v.compile(schema);

it("should give true if one valid object is given", () => {
let obj = { list: [
{
name: "Joe",
age: 34
}
]};
let res = check(obj);
expect(res).toBe(true);

let obj2 = { list: [
{
country: "germany",
code: "de"
}
]};
let res2 = check(obj2);
expect(res2).toBe(true);
});

it("should give true if three valid objects given", () => {
let obj = { list: [
{
name: "Joe",
age: 34
},
{
country: "germany",
code: "de"
},
{
country: "hungary",
code: "hu"
}
]};
let res = check(obj);
expect(res).toBe(true);
});

it("should give error if one object is broken", () => {
let obj = { list: [
{
name: "Joe",
age: 34
},
{
country: "germany",
},
{
country: "hungary",
code: "hu"
}
]};

let res = check(obj);

expect(res).toBeInstanceOf(Array);
expect(res.length).toBe(3);
expect(res[0].type).toBe("required");
expect(res[0].field).toBe("list[1].name");

expect(res[1].type).toBe("required");
expect(res[1].field).toBe("list[1].age");
});

it("should give error if one object is empty", () => {
let obj = { list: [
{
name: "Joe",
age: 34
},
{
country: "hungary",
code: "hu"
},
{
}
]};

let res = check(obj);

expect(res).toBeInstanceOf(Array);
expect(res.length).toBe(4);
expect(res[0].type).toBe("required");
expect(res[0].field).toBe("list[2].name");

expect(res[1].type).toBe("required");
expect(res[1].field).toBe("list[2].age");

});

});

describe("Test multiple rules with arrays", () => {
const v = new Validator();

let schema = {
list: [
{
type: "array",
items: "string"
},
{
type: "array",
items: "number"
}
]
};

let check = v.compile(schema);

it("should give true if first array is given", () => {
let obj = { list: ["hello", "there", "this", "is", "a", "test"] };

let res = check(obj);

expect(res).toBe(true);
});

it("should give true if second array is given", () => {
let obj = { list: [1, 3, 3, 7] };

let res = check(obj);

expect(res).toBe(true);
});

it("should give error if the array is broken", () => {
let obj = { list: ["hello", 3] };

let res = check(obj);

expect(res).toBeInstanceOf(Array);
expect(res.length).toBe(2);
expect(res[0].type).toBe("string");
expect(res[0].field).toBe("list[1]");

expect(res[1].type).toBe("number");
expect(res[1].field).toBe("list[0]");
});

it("should give error if the array is broken", () => {
let obj = { list: [true, false] };
let res = check(obj);

expect(res).toBeInstanceOf(Array);
expect(res.length).toBe(4);
expect(res[0].type).toBe("string");
expect(res[0].field).toBe("list[0]");

expect(res[1].type).toBe("string");
expect(res[1].field).toBe("list[1]");

});

});