diff --git a/Validator.test.ts b/Validator.test.ts index c962f85..3cad67c 100644 --- a/Validator.test.ts +++ b/Validator.test.ts @@ -9,6 +9,7 @@ import { isString, isNumber, isInteger, + isRequired, } from "./mod.ts"; Deno.test("validate schema (match)", async () => { @@ -17,6 +18,11 @@ Deno.test("validate schema (match)", async () => { ["string", [isString]], [["arr", "ay"], { [ArraySymbol]: isString }], [{ foo: 3.1415, lorem: "ipsum" }, { foo: isNumber, lorem: [isString] }], + [{}, { optional: [isString] }], + [{ foo: { bar: "" } }, { foo: { bar: [isRequired, isString] } }], + [{ foo: { bar: "" } }, { foo: { bar: [isString] } }], + [{ foo: {} }, { foo: { bar: [isString] } }], + [{}, { foo: { bar: [isString] } }], ]; for (const [value, constraints] of values) { assertEquals([], await validate(value, constraints)); @@ -29,6 +35,10 @@ Deno.test("validate schema (no match)", async () => { [false, [isString]], [["arr", ["ay"]], { [ArraySymbol]: isString }], [{ foo: 3.1415, lorem: "ipsum" }, { foo: isInteger, lorem: [isString] }], + [{}, { required: [isRequired, isString] }], + [{ foo: { bar: 1 } }, { foo: { bar: [isRequired, isString] } }], + [{ foo: {} }, { foo: { bar: [isRequired, isString] } }], + [{}, { foo: { bar: [isRequired, isString] } }], ]; for (const [value, constraints] of values) { assertNotEquals([], await validate(value, constraints)); diff --git a/Validator.ts b/Validator.ts index 35a786f..9a65feb 100644 --- a/Validator.ts +++ b/Validator.ts @@ -99,17 +99,8 @@ async function validateSchema( if (!validators.hasOwnProperty(prop)) { continue; } - if (value.hasOwnProperty(prop)) { - const errors = await validate(value[prop], v[prop]); - valErrors.push(...(errors ?? [])); - } else { - valErrors.push({ - type: "property", - param: [prop], - message: `Property '${prop}' expected but not found!`, - args: { property: prop }, - }); - } + const errors = await validate(value && value[prop], v[prop]); + valErrors.push(...(errors ?? [])); } return valErrors; } diff --git a/mod.ts b/mod.ts index e1fd508..2f6dfc5 100644 --- a/mod.ts +++ b/mod.ts @@ -8,4 +8,8 @@ export { } from "./Validator.ts"; export { isString } from "./validators/string.ts"; export { isNumber, isInteger } from "./validators/number.ts"; +export { isArray } from "./validators/array.ts"; export { or } from "./validators/logic.ts"; +export { isRequired, isDefined, isNotEmpty } from "./validators/empty.ts"; +export { isSymbol } from "./validators/symbol.ts"; +export { isBoolean } from "./validators/boolean.ts"; diff --git a/validators/array.test.ts b/validators/array.test.ts new file mode 100644 index 0000000..e634f0b --- /dev/null +++ b/validators/array.test.ts @@ -0,0 +1,38 @@ +import { isArray } from "./array.ts"; +import { validate } from "../mod.ts"; +import { + assertEquals, + assertNotEquals, +} from "https://deno.land/std@0.53.0/testing/asserts.ts"; + +Deno.test("isArray (match)", async () => { + const values = [ + undefined, + null, + [], + ["foo"], + ]; + for (const value of values) { + assertEquals(await validate(value, isArray), []); + } +}); + +Deno.test("isArray (no match)", async () => { + const values = [ + 0, + 1, + false, + true, + "", + "foo", + new String(), + new String("bar"), + () => {}, + function named() {}, + new Object(), + Symbol(), + ]; + for (const value of values) { + assertNotEquals(await validate(value, isArray), []); + } +}); diff --git a/validators/array.ts b/validators/array.ts new file mode 100644 index 0000000..e3db194 --- /dev/null +++ b/validators/array.ts @@ -0,0 +1,14 @@ +import { Validator, Args } from "../mod.ts"; + +export const isArray: Validator = { + type: "isArray", + check: (value: any) => { + if (value === null || value === undefined) return; + if (!Array.isArray(value)) { + return {}; + } + }, + message: (value: any, args?: Args) => { + return `This value has to be an array.`; + }, +}; diff --git a/validators/boolean.test.ts b/validators/boolean.test.ts new file mode 100644 index 0000000..48725d7 --- /dev/null +++ b/validators/boolean.test.ts @@ -0,0 +1,36 @@ +import { isBoolean } from "./boolean.ts"; +import { validate } from "../mod.ts"; +import { + assertEquals, + assertNotEquals, +} from "https://deno.land/std@0.53.0/testing/asserts.ts"; + +Deno.test("isBoolean (match)", async () => { + const values = [ + undefined, + null, + true, + false, + ]; + for (const value of values) { + assertEquals(await validate(value, isBoolean), []); + } +}); + +Deno.test("isBoolean (no match)", async () => { + const values = [ + 0, + 1, + "", + "foo", + new String(), + new String("bar"), + () => {}, + function named() {}, + new Object(), + Symbol(), + ]; + for (const value of values) { + assertNotEquals(await validate(value, isBoolean), []); + } +}); diff --git a/validators/boolean.ts b/validators/boolean.ts new file mode 100644 index 0000000..d9c787a --- /dev/null +++ b/validators/boolean.ts @@ -0,0 +1,14 @@ +import { Validator, Args } from "../mod.ts"; + +export const isBoolean: Validator = { + type: "isBoolean", + check: (value: any) => { + if (value === null || value === undefined) return; + if (value !== true && value !== false) { + return {}; + } + }, + message: (value: any, args?: Args) => { + return `This value has to be a boolean.`; + }, +}; diff --git a/validators/empty.test.ts b/validators/empty.test.ts new file mode 100644 index 0000000..9d39139 --- /dev/null +++ b/validators/empty.test.ts @@ -0,0 +1,99 @@ +import { isRequired, isDefined, isNotEmpty } from "./empty.ts"; +import { validate } from "../mod.ts"; +import { + assertEquals, + assertNotEquals, +} from "https://deno.land/std@0.53.0/testing/asserts.ts"; + +Deno.test("isRequired (match)", async () => { + const values = [ + "", + " ", + "foo", + 0, + 1, + true, + false, + NaN, + {}, + { foo: "bar" }, + [], + ["foo"], + Symbol("foo"), + ]; + for (const value of values) { + assertEquals(await validate(value, isRequired), []); + } +}); + +Deno.test("isRequired (no match)", async () => { + const values = [ + undefined, + null, + ]; + for (const value of values) { + assertNotEquals(await validate(value, isRequired), []); + } +}); + +Deno.test("isDefined (match)", async () => { + const values = [ + null, + "", + " ", + "foo", + 0, + 1, + true, + false, + NaN, + {}, + { foo: "bar" }, + [], + ["foo"], + Symbol("foo"), + ]; + for (const value of values) { + assertEquals(await validate(value, isDefined), []); + } +}); + +Deno.test("isDefined (no match)", async () => { + const values = [ + undefined, + ]; + for (const value of values) { + assertNotEquals(await validate(value, isDefined), []); + } +}); + +Deno.test("isNotEmpty (match)", async () => { + const values = [ + "foo", + 0, + 1, + true, + false, + NaN, + { foo: "bar" }, + ["foo"], + Symbol("foo"), + ]; + for (const value of values) { + assertEquals(await validate(value, isNotEmpty), []); + } +}); + +Deno.test("isNotEmpty (no match)", async () => { + const values = [ + undefined, + null, + "", + " ", + {}, + [], + ]; + for (const value of values) { + assertNotEquals(await validate(value, isNotEmpty), []); + } +}); diff --git a/validators/empty.ts b/validators/empty.ts new file mode 100644 index 0000000..b477974 --- /dev/null +++ b/validators/empty.ts @@ -0,0 +1,70 @@ +import { + Validator, + Args, + isString, + isArray, + isNumber, + isSymbol, + isBoolean, +} from "../mod.ts"; + +export const isRequired: Validator = { + type: "isRequired", + check: (value: any) => { + if (value === null || value === undefined) { + return {}; + } + }, + message: (value: any, args?: Args) => { + return `This value is required.`; + }, +}; + +export const isDefined: Validator = { + type: "isDefined", + check: (value: any) => { + if (value === undefined) { + return {}; + } + }, + message: (value: any, args?: Args) => { + return `This value has to be defined.`; + }, +}; + +export const isNotEmpty: Validator = { + type: "isNotEmpty", + check: async (value: any) => { + if (value === null || value === undefined) { + return {}; + } + if ((await isString.check(value)) === undefined) { + if (/^\s*$/.test(value)) { + return {}; + } + return; + } + if ((await isNumber.check(value)) === undefined || Number.isNaN(value)) { + return; + } + if (await isSymbol.check(value) === undefined) { + return; + } + if (await isBoolean.check(value) === undefined) { + return; + } + if ((await isArray.check(value)) === undefined) { + if (value.length > 0) { + return; + } + return {}; + } + for (const key in value) { + return; + } + return {}; + }, + message: (value: any, args?: Args) => { + return `This value has to be non-empty.`; + }, +}; diff --git a/validators/number.ts b/validators/number.ts index c53acaf..6fb7c0b 100644 --- a/validators/number.ts +++ b/validators/number.ts @@ -9,7 +9,7 @@ export const isNumber: Validator = { } }, message: (value: any, args?: Args) => { - return `The value '${value && value.toString()}' has to be a number.`; + return `This value has to be a number.`; }, }; @@ -22,6 +22,6 @@ export const isInteger: Validator = { } }, message: (value: any, args?: Args) => { - return `The value '${value && value.toString()}' has to be an integer.`; + return `This value has to be an integer.`; }, }; diff --git a/validators/string.ts b/validators/string.ts index 3d84c3d..f26a85e 100644 --- a/validators/string.ts +++ b/validators/string.ts @@ -9,6 +9,6 @@ export const isString: Validator = { } }, message: (value: any, args?: Args) => { - return `The value '${value && value.toString()}' has to be a string.`; + return `This value has to be a string.`; }, }; diff --git a/validators/symbol.test.ts b/validators/symbol.test.ts new file mode 100644 index 0000000..bfd309e --- /dev/null +++ b/validators/symbol.test.ts @@ -0,0 +1,36 @@ +import { isSymbol } from "./symbol.ts"; +import { validate } from "../mod.ts"; +import { + assertEquals, + assertNotEquals, +} from "https://deno.land/std@0.53.0/testing/asserts.ts"; + +Deno.test("isSymbol (match)", async () => { + const values = [ + undefined, + null, + Symbol(), + ]; + for (const value of values) { + assertEquals(await validate(value, isSymbol), []); + } +}); + +Deno.test("isSymbol (no match)", async () => { + const values = [ + 0, + 1, + true, + false, + "", + "foo", + new String(), + new String("bar"), + () => {}, + function named() {}, + new Object(), + ]; + for (const value of values) { + assertNotEquals(await validate(value, isSymbol), []); + } +}); diff --git a/validators/symbol.ts b/validators/symbol.ts new file mode 100644 index 0000000..35b80db --- /dev/null +++ b/validators/symbol.ts @@ -0,0 +1,14 @@ +import { Validator, Args } from "../mod.ts"; + +export const isSymbol: Validator = { + type: "isSymbol", + check: (value: any) => { + if (value === null || value === undefined) return; + if (typeof value !== "symbol") { + return {}; + } + }, + message: (value: any, args?: Args) => { + return `This value has to be a symbol.`; + }, +};