Skip to content

Commit

Permalink
feat(ValidationRules): add number validation rules
Browse files Browse the repository at this point in the history
Add common validation rules for numeric properties.

resolves #440
  • Loading branch information
CuddleBunny committed Feb 20, 2019
1 parent 76e97b3 commit f67cf59
Show file tree
Hide file tree
Showing 3 changed files with 136 additions and 0 deletions.
3 changes: 3 additions & 0 deletions src/implementation/validation-messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ export const validationMessages: ValidationMessages = {
maxLength: `\${$displayName} cannot be longer than \${$config.length} character\${$config.length === 1 ? '' : 's'}.`,
minItems: `\${$displayName} must contain at least \${$config.count} item\${$config.count === 1 ? '' : 's'}.`,
maxItems: `\${$displayName} cannot contain more than \${$config.count} item\${$config.count === 1 ? '' : 's'}.`,
min: `\${$displayName} must be at least \${$config.constraint}.`,
max: `\${$displayName} must be at most \${$config.constraint}.`,
range: `\${$displayName} must be between \${$config.min} and \${$config.max}.`,
equals: `\${$displayName} must be \${$config.expectedValue}.`,
};

Expand Down
58 changes: 58 additions & 0 deletions src/implementation/validation-rules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,30 @@ export class FluentRuleCustomizer<TObject, TValue> {
return this.fluentRules.maxItems(count);
}

/**
* Applies the "min" NUMBER validation rule to the property.
* null and undefined values are considered valid.
*/
public min(value: number) {
return this.fluentRules.min(value);
}

/**
* Applies the "max" NUMBER validation rule to the property.
* null and undefined values are considered valid.
*/
public max(value: number) {
return this.fluentRules.max(value);
}

/**
* Applies the "range" NUMBER validation rule to the property.
* null and undefined values are considered valid.
*/
public range(min: number, max: number, inclusive: boolean = true) {
return this.fluentRules.range(min, max, inclusive);
}

/**
* Applies the "equals" validation rule to the property.
* null, undefined and empty-string values are considered valid.
Expand Down Expand Up @@ -336,6 +360,40 @@ export class FluentRules<TObject, TValue> {
.withMessageKey('maxItems');
}

/**
* Applies the "min" NUMBER validation rule to the property.
* null and undefined values are considered valid.
*/
public min(constraint: number) {
return this.satisfies((value: any) => value === null || value === undefined || value >= constraint, { constraint })
.withMessageKey('min');
}

/**
* Applies the "max" NUMBER validation rule to the property.
* null and undefined values are considered valid.
*/
public max(constraint: number) {
return this.satisfies((value: any) => value === null || value === undefined || value <= constraint, { constraint })
.withMessageKey('max');
}

/**
* Applies the "range" NUMBER validation rule to the property.
* null and undefined values are considered valid.
*/
public range(min: number, max: number, inclusive: boolean = true) {
if (inclusive) {
return this.satisfies((value: any) => value === null || value === undefined || (value >= min && value <= max),
{ min, max, inclusive })
.withMessageKey('range');
} else {
return this.satisfies((value: any) => value === null || value === undefined || (value > min && value < max),
{ min, max, inclusive })
.withMessageKey('range');
}
}

/**
* Applies the "equals" validation rule to the property.
* null and undefined values are considered valid.
Expand Down
75 changes: 75 additions & 0 deletions test/validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,81 @@ describe('Validator', () => {
.then(done);
});

it('validates numeric properties', (done: () => void) => {
const obj = { value: 1 };
let rules = ValidationRules.ensure('value').min(1).rules;
validator.validateObject(obj, rules)
.then(results => {
const expected = [new ValidateResult(rules[0][0], obj, 'value', true, null)];
expected[0].id = results[0].id;
expect(results).toEqual(expected);
})
.then(() => {
rules = ValidationRules.ensure('value').max(1).rules;
return validator.validateObject(obj, rules);
})
.then(results => {
const expected = [new ValidateResult(rules[0][0], obj, 'value', true, null)];
expected[0].id = results[0].id;
expect(results).toEqual(expected);
})
.then(() => {
rules = ValidationRules.ensure('value').range(0, 1).rules;
return validator.validateObject(obj, rules);
})
.then(results => {
const expected = [new ValidateResult(rules[0][0], obj, 'value', true, null)];
expected[0].id = results[0].id;
expect(results).toEqual(expected);
})
.then(() => {
rules = ValidationRules.ensure('value').range(0, 2, false).rules;
return validator.validateObject(obj, rules);
})
.then(results => {
const expected = [new ValidateResult(rules[0][0], obj, 'value', true, null)];
expected[0].id = results[0].id;
expect(results).toEqual(expected);
})
.then(() => {
rules = ValidationRules.ensure('value').min(2).rules;
return validator.validateObject(obj, rules);
})
.then(results => {
const expected = [new ValidateResult(rules[0][0], obj, 'value', false, 'Value must be at least 2.')];
expected[0].id = results[0].id;
expect(results).toEqual(expected);
})
.then(() => {
rules = ValidationRules.ensure('value').max(0).rules;
return validator.validateObject(obj, rules);
})
.then(results => {
const expected = [new ValidateResult(rules[0][0], obj, 'value', false, 'Value must be at most 0.')];
expected[0].id = results[0].id;
expect(results).toEqual(expected);
})
.then(() => {
rules = ValidationRules.ensure('value').range(2, 3).rules;
return validator.validateObject(obj, rules);
})
.then(results => {
const expected = [new ValidateResult(rules[0][0], obj, 'value', false, 'Value must be between 2 and 3.')];
expected[0].id = results[0].id;
expect(results).toEqual(expected);
})
.then(() => {
rules = ValidationRules.ensure('value').range(1, 3, false).rules;
return validator.validateObject(obj, rules);
})
.then(results => {
const expected = [new ValidateResult(rules[0][0], obj, 'value', false, 'Value must be between 1 and 3.')];
expected[0].id = results[0].id;
expect(results).toEqual(expected);
})
.then(done);
});

it('handles numeric properties', (done: () => void) => {
const objStr = {} as any;
objStr['2'] = 'test';
Expand Down

0 comments on commit f67cf59

Please sign in to comment.