import {Validator} from "../../src/validation/Validator"; import {ValidationArguments} from "../../src/validation/ValidationArguments"; import {registerDecorator} from "../../src/register-decorator"; import {ValidationOptions} from "../../src/decorator/ValidationOptions"; import {ValidatorConstraint} from "../../src/decorator/decorators"; import {ValidatorConstraintInterface} from "../../src/validation/ValidatorConstraintInterface"; const validator = new Validator(); describe("decorator with inline validation", () => { function IsLongerThan(property: string, validationOptions?: ValidationOptions) { return function(object: object, propertyName: string): void { registerDecorator({ target: object.constructor, propertyName: propertyName, options: validationOptions, constraints: [property], name: "isLongerThan", validator: { validate(value: any, args: ValidationArguments): Promise | boolean { const [relatedPropertyName] = args.constraints; const relatedValue = (args.object as any)[relatedPropertyName]; if (relatedValue === undefined || relatedValue === null) { return true; } const result = typeof value === "string" && typeof relatedValue === "string" && value.length > relatedValue.length; const asPromise = validationOptions && validationOptions.context && validationOptions.context.promise; return asPromise ? Promise.resolve(result) : result; } } }); }; } class MyClass { @IsLongerThan("lastName", { context: {foo: "bar"}, message: "$property must be longer then $constraint1. Given value: $value" }) firstName: string; lastName: string; } class MyClassWithAsyncValidator { @IsLongerThan("lastName", { context: {foo: "bar", promise: true}, message: "$property must be longer then $constraint1. Given value: $value" }) firstName: string; lastName: string; } it("if firstName is not empty and lastLame is empty then it should succeed", () => { expect.assertions(1); const model = new MyClass(); model.firstName = "hell no world"; return validator.validate(model).then(errors => { expect(errors.length).toEqual(0); }); }); it("if firstName is empty and lastLame is not empty then it should fail", () => { expect.assertions(2); const model = new MyClass(); model.firstName = ""; model.lastName = "Kim"; return validator.validate(model).then(errors => { expect(errors.length).toEqual(1); expect(errors[0].constraints).toEqual({isLongerThan: "firstName must be longer then lastName. Given value: "}); }); }); it("if firstName is shorter then lastLame then it should fail", () => { expect.assertions(2); const model = new MyClass(); model.firstName = "Li"; model.lastName = "Kim"; return validator.validate(model).then(errors => { expect(errors.length).toEqual(1); expect(errors[0].constraints).toEqual({isLongerThan: "firstName must be longer then lastName. Given value: Li"}); }); }); it("should include context", () => { expect.assertions(4); const model = new MyClass(); const asyncModel = new MyClassWithAsyncValidator(); model.firstName = asyncModel.firstName = "Paul"; model.lastName = asyncModel.lastName = "Walker"; return validator.validate(model).then(errors => { expect(errors.length).toEqual(1); expect(errors[0].contexts).toEqual({isLongerThan: {foo: "bar"}}); return validator.validate(asyncModel).then(errors => { expect(errors.length).toEqual(1); expect(errors[0].contexts).toHaveProperty("isLongerThan.foo", "bar"); }); }); }); }); describe("decorator with default message", () => { function IsLonger(property: string, validationOptions?: ValidationOptions) { return function(object: object, propertyName: string): void { registerDecorator({ target: object.constructor, propertyName: propertyName, options: validationOptions, constraints: [property], name: "isLonger", validator: { validate(value: any, args: ValidationArguments): boolean { const [relatedPropertyName] = args.constraints; const relatedValue = (args.object as any)[relatedPropertyName]; if (relatedValue === undefined || relatedValue === null) return true; return typeof value === "string" && typeof relatedValue === "string" && value.length > relatedValue.length; }, defaultMessage(args: ValidationArguments): string { return args.property + " must be longer then " + args.constraints[0]; } } }); }; } class SecondClass { @IsLonger("lastName") firstName: string; lastName: string; } it("if firstName is not empty and lastLame is empty then it should succeed", () => { expect.assertions(1); const model = new SecondClass(); model.firstName = "hell no world"; return validator.validate(model).then(errors => { expect(errors.length).toEqual(0); }); }); it("if firstName is empty and lastLame is not empty then it should fail", () => { expect.assertions(2); const model = new SecondClass(); model.firstName = ""; model.lastName = "Kim"; return validator.validate(model).then(errors => { expect(errors.length).toEqual(1); expect(errors[0].constraints).toEqual({isLonger: "firstName must be longer then lastName"}); }); }); it("if firstName is shorter then lastLame then it should fail", () => { expect.assertions(2); const model = new SecondClass(); model.firstName = "Li"; model.lastName = "Kim"; return validator.validate(model).then(errors => { expect(errors.length).toEqual(1); expect(errors[0].constraints).toEqual({isLonger: "firstName must be longer then lastName"}); }); }); }); describe("decorator with separate validation constraint class", () => { @ValidatorConstraint({name: "isShortenThan"}) class IsShortenThanConstraint implements ValidatorConstraintInterface { validate(value: any, args: ValidationArguments): boolean { const [relatedPropertyName] = args.constraints; const relatedValue = (args.object as any)[relatedPropertyName]; if (value === null || value === undefined) return true; return typeof value === "string" && typeof relatedValue === "string" && value.length < relatedValue.length; } } function IsShorterThan(property: string, validationOptions?: ValidationOptions) { return function(object: object, propertyName: string): void { registerDecorator({ target: object.constructor, propertyName: propertyName, options: validationOptions, constraints: [property], validator: IsShortenThanConstraint }); }; } class MyClass { firstName: string; @IsShorterThan("firstName", { message: "$property must be shorter then $constraint1. Given value: $value" }) lastName: string; } it("if firstName is not empty and lastLame is empty then it should succeed", () => { expect.assertions(1); const model = new MyClass(); model.firstName = "hell no world"; return validator.validate(model).then(errors => { expect(errors.length).toEqual(0); }); }); it("if firstName is empty and lastLame is not empty then it should fail", () => { expect.assertions(2); const model = new MyClass(); model.firstName = ""; model.lastName = "Kim"; return validator.validate(model).then(errors => { expect(errors.length).toEqual(1); expect(errors[0].constraints).toEqual({isShortenThan: "lastName must be shorter then firstName. Given value: Kim"}); }); }); it("if firstName is shorter then lastLame then it should fail", () => { expect.assertions(2); const model = new MyClass(); model.firstName = "Li"; model.lastName = "Kim"; return validator.validate(model).then(errors => { expect(errors.length).toEqual(1); expect(errors[0].constraints).toEqual({isShortenThan: "lastName must be shorter then firstName. Given value: Kim"}); }); }); });