|  | 
| 1 | 1 | import "es6-shim"; | 
| 2 |  | -import {Contains, Matches, MinLength, ValidateNested} from "../../src/decorator/decorators"; | 
|  | 2 | +import {Contains, Matches, MinLength, ValidateNested, ValidatorConstraint, Validate } from "../../src/decorator/decorators"; | 
| 3 | 3 | import {Validator} from "../../src/validation/Validator"; | 
| 4 |  | -import {ValidationError} from "../../src"; | 
|  | 4 | +import {ValidationError, ValidatorConstraintInterface} from "../../src"; | 
| 5 | 5 | 
 | 
| 6 |  | -import {should, use } from "chai"; | 
|  | 6 | +import {should, use} from "chai"; | 
| 7 | 7 | 
 | 
| 8 | 8 | import * as chaiAsPromised from "chai-as-promised"; | 
| 9 | 9 | 
 | 
| @@ -163,6 +163,110 @@ describe("validation options", function() { | 
| 163 | 163 |             }); | 
| 164 | 164 |         }); | 
| 165 | 165 | 
 | 
|  | 166 | +        it("should apply validation via custom constraint class to array items (but not array itself)", function() { | 
|  | 167 | +            @ValidatorConstraint({ name: "customIsNotArrayConstraint", async: false }) | 
|  | 168 | +            class CustomIsNotArrayConstraint implements ValidatorConstraintInterface { | 
|  | 169 | +                validate(value: any) { | 
|  | 170 | +                    return !(value instanceof Array); | 
|  | 171 | +                } | 
|  | 172 | +            } | 
|  | 173 | + | 
|  | 174 | +            class MyClass { | 
|  | 175 | +                @Validate(CustomIsNotArrayConstraint, { | 
|  | 176 | +                    each: true | 
|  | 177 | +                }) | 
|  | 178 | +                someArrayOfNonArrayItems: string[]; | 
|  | 179 | +            } | 
|  | 180 | + | 
|  | 181 | +            const model = new MyClass(); | 
|  | 182 | +            model.someArrayOfNonArrayItems = ["not array", "also not array", "not array at all"]; | 
|  | 183 | +            return validator.validate(model).then(errors => { | 
|  | 184 | +                errors.length.should.be.equal(0); | 
|  | 185 | +            }); | 
|  | 186 | +        }); | 
|  | 187 | + | 
|  | 188 | +        it("should apply validation via custom constraint class with synchronous logic to each item in the array", function() { | 
|  | 189 | +            @ValidatorConstraint({ name: "customContainsHelloConstraint", async: false }) | 
|  | 190 | +            class CustomContainsHelloConstraint implements ValidatorConstraintInterface { | 
|  | 191 | +                validate(value: any) { | 
|  | 192 | +                    return !(value instanceof Array) && String(value).includes("hello"); | 
|  | 193 | +                } | 
|  | 194 | +            } | 
|  | 195 | + | 
|  | 196 | +            class MyClass { | 
|  | 197 | +                @Validate(CustomContainsHelloConstraint, { | 
|  | 198 | +                    each: true | 
|  | 199 | +                }) | 
|  | 200 | +                someProperty: string[]; | 
|  | 201 | +            } | 
|  | 202 | + | 
|  | 203 | +            const model = new MyClass(); | 
|  | 204 | +            model.someProperty = ["hell no world", "hello", "helo world", "hello world", "hello dear friend"]; | 
|  | 205 | +            return validator.validate(model).then(errors => { | 
|  | 206 | +                errors.length.should.be.equal(1); | 
|  | 207 | +                errors[0].constraints.should.be.eql({ customContainsHelloConstraint: "" }); | 
|  | 208 | +                errors[0].value.should.be.equal(model.someProperty); | 
|  | 209 | +                errors[0].target.should.be.equal(model); | 
|  | 210 | +                errors[0].property.should.be.equal("someProperty"); | 
|  | 211 | +            }); | 
|  | 212 | +        }); | 
|  | 213 | + | 
|  | 214 | +        it("should apply validation via custom constraint class with async logic to each item in the array", function() { | 
|  | 215 | +            @ValidatorConstraint({ name: "customAsyncContainsHelloConstraint", async: true }) | 
|  | 216 | +            class CustomAsyncContainsHelloConstraint implements ValidatorConstraintInterface { | 
|  | 217 | +                validate(value: any) { | 
|  | 218 | +                    const isValid = !(value instanceof Array) && String(value).includes("hello"); | 
|  | 219 | + | 
|  | 220 | +                    return Promise.resolve(isValid); | 
|  | 221 | +                } | 
|  | 222 | +            } | 
|  | 223 | + | 
|  | 224 | +            class MyClass { | 
|  | 225 | +                @Validate(CustomAsyncContainsHelloConstraint, { | 
|  | 226 | +                    each: true | 
|  | 227 | +                }) | 
|  | 228 | +                someProperty: string[]; | 
|  | 229 | +            } | 
|  | 230 | + | 
|  | 231 | +            const model = new MyClass(); | 
|  | 232 | +            model.someProperty = ["hell no world", "hello", "helo world", "hello world", "hello dear friend"]; | 
|  | 233 | +            return validator.validate(model).then(errors => { | 
|  | 234 | +                errors.length.should.be.equal(1); | 
|  | 235 | +                errors[0].constraints.should.be.eql({ customAsyncContainsHelloConstraint: "" }); | 
|  | 236 | +                errors[0].value.should.be.equal(model.someProperty); | 
|  | 237 | +                errors[0].target.should.be.equal(model); | 
|  | 238 | +                errors[0].property.should.be.equal("someProperty"); | 
|  | 239 | +            }); | 
|  | 240 | +        }); | 
|  | 241 | + | 
|  | 242 | +        it("should apply validation via custom constraint class with mixed (synchronous + async) logic to each item in the array", function() { | 
|  | 243 | +            @ValidatorConstraint({ name: "customMixedContainsHelloConstraint", async: true }) | 
|  | 244 | +            class CustomMixedContainsHelloConstraint implements ValidatorConstraintInterface { | 
|  | 245 | +                validate(value: any) { | 
|  | 246 | +                    const isValid = !(value instanceof Array) && String(value).includes("hello"); | 
|  | 247 | + | 
|  | 248 | +                    return isValid ? isValid : Promise.resolve(isValid); | 
|  | 249 | +                } | 
|  | 250 | +            } | 
|  | 251 | + | 
|  | 252 | +            class MyClass { | 
|  | 253 | +                @Validate(CustomMixedContainsHelloConstraint, { | 
|  | 254 | +                    each: true | 
|  | 255 | +                }) | 
|  | 256 | +                someProperty: string[]; | 
|  | 257 | +            } | 
|  | 258 | + | 
|  | 259 | +            const model = new MyClass(); | 
|  | 260 | +            model.someProperty = ["hell no world", "hello", "helo world", "hello world", "hello dear friend"]; | 
|  | 261 | +            return validator.validate(model).then(errors => { | 
|  | 262 | +                errors.length.should.be.equal(1); | 
|  | 263 | +                errors[0].constraints.should.be.eql({ customMixedContainsHelloConstraint: "" }); | 
|  | 264 | +                errors[0].value.should.be.equal(model.someProperty); | 
|  | 265 | +                errors[0].target.should.be.equal(model); | 
|  | 266 | +                errors[0].property.should.be.equal("someProperty"); | 
|  | 267 | +            }); | 
|  | 268 | +        }); | 
|  | 269 | + | 
| 166 | 270 |     }); | 
| 167 | 271 | 
 | 
| 168 | 272 |     describe("groups", function() { | 
|  | 
0 commit comments