-
Notifications
You must be signed in to change notification settings - Fork 20
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add createValidatorFactory for validators with addl args
- Loading branch information
Showing
12 changed files
with
262 additions
and
113 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
// @flow | ||
import createValidatorFactory from '../src/createValidatorFactory'; | ||
|
||
const beginsWithDefinition = (message, c: string) => (value) => { | ||
const regex = new RegExp(`^${c}`, 'i'); | ||
|
||
if (value && !regex.test(value)) { | ||
return message; | ||
} | ||
}; | ||
|
||
const beginsWith = createValidatorFactory( | ||
beginsWithDefinition, | ||
(field, c: string) => `${field} must start with ${c}`, | ||
); | ||
|
||
const isBetweenDefinition = (message, x: number, y: number) => (value) => { | ||
const n = Number(value); | ||
|
||
if (n < x || n > y) { | ||
return message; | ||
} | ||
}; | ||
|
||
const isBetween = createValidatorFactory( | ||
isBetweenDefinition, | ||
(field, x: number, y: number) => `${field} must be between ${x} and ${y}`, | ||
); | ||
|
||
const beginsWithA = beginsWith('A'); | ||
const isBetween1And10 = isBetween(1, 10); | ||
|
||
it('returns error message for incorrect values', () => { | ||
expect(beginsWithA('Foo')('bar')).toBe('Foo must start with A'); | ||
expect(isBetween1And10('Foo')('11')).toBe('Foo must be between 1 and 10'); | ||
}); | ||
|
||
it('returns undefined for correct values', () => { | ||
expect(beginsWithA('Foo')('abc')).toBe(undefined); | ||
expect(isBetween1And10('Foo')('5')).toBe(undefined); | ||
}); | ||
|
||
it('factories are curried', () => { | ||
const initial: ValidatorFactory = isBetween(1); | ||
const isBetween1And5 = initial(5); | ||
|
||
expect(isBetween1And5('Foo')('2')).toBe(undefined); | ||
expect(isBetween1And5('Foo')('6')).toBe('Foo must be between 1 and 5'); | ||
}); | ||
|
||
it('validators can use a plain string message', () => { | ||
const message = 'Must be valid'; | ||
const factory = createValidatorFactory(beginsWithDefinition, message); | ||
const validator = factory('A')(); | ||
|
||
expect(validator('foo')).toBe(message); | ||
}); | ||
|
||
it('can specify numArgs for optional args', () => { | ||
const DEFAULT_Y = 1000; | ||
|
||
const factory = createValidatorFactory({ | ||
numArgs: 1, | ||
|
||
definition: (message, x: number, y: number = DEFAULT_Y) => (value) => { | ||
const n = Number(value); | ||
|
||
if (n < x || n > y) { | ||
return message; | ||
} | ||
}, | ||
|
||
messageCreator: (field, x: number, y: number = DEFAULT_Y) => `${field} must be between ${x} and ${y}`, | ||
}); | ||
|
||
const isBetween1And1000 = factory(1)('Foo'); | ||
const isBetween1And5 = factory(1, 5)('Foo'); | ||
|
||
expect(isBetween1And1000('500')).toBe(undefined); | ||
expect(isBetween1And5('2')).toBe(undefined); | ||
|
||
expect(isBetween1And1000('1001')).toBe('Foo must be between 1 and 1000'); | ||
expect(isBetween1And5('6')).toBe('Foo must be between 1 and 5'); | ||
}); | ||
|
||
it('creating requires a string or function message creator', () => { | ||
const errorMessage = 'Please provide a message string or message creator function'; | ||
|
||
expect(_ => createValidatorFactory(beginsWithDefinition)).toThrowError(errorMessage); | ||
expect(_ => createValidatorFactory(beginsWithDefinition, 'foo')).not.toThrow(); | ||
}); | ||
|
||
it('requires a string or configuration object', () => { | ||
const errorMessage = ( | ||
'Please provide a string or configuration object with a `field` or ' + | ||
'`message` property' | ||
); | ||
|
||
expect(_ => beginsWithA()).toThrowError(errorMessage); | ||
expect(_ => beginsWithA({})).toThrowError(errorMessage); | ||
expect(_ => beginsWithA('My Field')).not.toThrow(); | ||
expect(_ => beginsWithA({ field: 'My Field' })).not.toThrow(); | ||
}); | ||
|
||
it('returns the message with the field as config option for an invalid value', () => { | ||
const expected = 'Foo must start with A'; | ||
|
||
expect(beginsWithA({ field: 'Foo' })('foo')).toBe(expected); | ||
}); | ||
|
||
it('uses the overriding message for an invalid value', () => { | ||
const message = 'Invalid Value'; | ||
|
||
expect(beginsWithA({ message })('foo')).toBe(message); | ||
}); | ||
|
||
it('uses the defaultMessageCreator if it is a string and config only has field', () => { | ||
const defaultMessageCreator = 'hello'; | ||
|
||
const validator = createValidatorFactory( | ||
message => value => !value && message, | ||
defaultMessageCreator, | ||
)()({ field: 'Foo' }); | ||
|
||
expect(validator()).toBe(defaultMessageCreator); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
// @flow | ||
import curry from 'lodash/curry'; | ||
import createValidator from './createValidator'; | ||
|
||
export default function createValidatorFactory( | ||
curriedDefinition: ValidatorImpl | ValidatorFactoryConfig, | ||
defaultMessageCreator?: MessageCreator, | ||
): ValidatorFactory { | ||
let finalCurriedDefinition; | ||
let finalMessageCreator; | ||
let numArgs; | ||
|
||
if (typeof curriedDefinition === 'function') { | ||
finalCurriedDefinition = curriedDefinition; | ||
finalMessageCreator = defaultMessageCreator; | ||
} else { | ||
finalCurriedDefinition = curriedDefinition.definition; | ||
finalMessageCreator = curriedDefinition.messageCreator; | ||
numArgs = curriedDefinition.numArgs; | ||
} | ||
|
||
// Duplicated with createValidator for flow | ||
if ( | ||
finalMessageCreator == null || | ||
(typeof finalMessageCreator !== 'string' && typeof finalMessageCreator !== 'function') | ||
) { | ||
throw new Error('Please provide a message string or message creator function'); | ||
} | ||
|
||
if (typeof numArgs === 'undefined') { | ||
numArgs = finalCurriedDefinition.length - 1; | ||
} | ||
|
||
return curry((...args) => ( | ||
createValidator(finalCurriedDefinition, finalMessageCreator, ...args) | ||
), numArgs); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,17 +1,18 @@ | ||
// @flow | ||
import createValidator from '../../createValidator'; | ||
|
||
export function validateMatchesPattern(regex: RegExp, message: any, value: string) { | ||
if (value && !regex.test(value)) { | ||
return message; | ||
} | ||
} | ||
|
||
export default function internalMatchesPattern( | ||
regex: RegExp, | ||
messageCreator: MessageCreator, | ||
): ConfigurableValidator { | ||
return createValidator( | ||
message => value => { | ||
if (value && !regex.test(value)) { | ||
return message; | ||
} | ||
}, | ||
|
||
message => value => validateMatchesPattern(regex, message, value), | ||
messageCreator, | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,17 +1,12 @@ | ||
// @flow | ||
import createValidator from '../createValidator'; | ||
import createValidatorFactory from '../createValidatorFactory'; | ||
|
||
export default function hasLengthBetween( | ||
min: number, | ||
max: number, | ||
): ConfigurableValidator { | ||
return createValidator( | ||
message => value => { | ||
if (value && (value.length < min || value.length > max)) { | ||
return message; | ||
} | ||
}, | ||
export default createValidatorFactory( | ||
(message, min: number, max: number) => value => { | ||
if (value && (value.length < min || value.length > max)) { | ||
return message; | ||
} | ||
}, | ||
|
||
field => `${field} must be between ${min} and ${max} characters long`, | ||
); | ||
} | ||
(field, min: number, max: number) => `${field} must be between ${min} and ${max} characters long`, | ||
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,16 +1,12 @@ | ||
// @flow | ||
import createValidator from '../createValidator'; | ||
import createValidatorFactory from '../createValidatorFactory'; | ||
|
||
export default function hasLengthGreaterThan( | ||
min: number, | ||
): ConfigurableValidator { | ||
return createValidator( | ||
message => value => { | ||
if (value && value.length <= min) { | ||
return message; | ||
} | ||
}, | ||
export default createValidatorFactory( | ||
(message, min: number) => value => { | ||
if (value && value.length <= min) { | ||
return message; | ||
} | ||
}, | ||
|
||
field => `${field} must be longer than ${min} characters`, | ||
); | ||
} | ||
(field, min: number) => `${field} must be longer than ${min} characters`, | ||
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,16 +1,12 @@ | ||
// @flow | ||
import createValidator from '../createValidator'; | ||
import createValidatorFactory from '../createValidatorFactory'; | ||
|
||
export default function hasLengthLessThan( | ||
max: number, | ||
): ConfigurableValidator { | ||
return createValidator( | ||
message => value => { | ||
if (value && value.length >= max) { | ||
return message; | ||
} | ||
}, | ||
export default createValidatorFactory( | ||
(message, max: number) => value => { | ||
if (value && value.length >= max) { | ||
return message; | ||
} | ||
}, | ||
|
||
field => `${field} cannot be longer than ${max} characters`, | ||
); | ||
} | ||
(field, max: number) => `${field} cannot be longer than ${max} characters`, | ||
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,31 +1,26 @@ | ||
// @flow | ||
import findIndex from 'lodash/findIndex'; | ||
import createValidator from '../createValidator'; | ||
import createValidatorFactory from '../createValidatorFactory'; | ||
|
||
const defaultComparer = (value: any, optionValue: any) => value === optionValue; | ||
|
||
export default function isOneOf<T>( | ||
values: Array<T>, | ||
comparer: Comparer = defaultComparer, | ||
): ConfigurableValidator { | ||
const valuesClone = values.slice(0); | ||
export default createValidatorFactory( | ||
(message, values: Array<any>, comparer: Comparer = defaultComparer) => value => { | ||
const valuesClone = values.slice(0); | ||
|
||
return createValidator( | ||
message => (value: T) => { | ||
if (value === undefined) { | ||
return; | ||
} | ||
if (value === undefined) { | ||
return; | ||
} | ||
|
||
const valueIndex = findIndex( | ||
valuesClone, | ||
optionValue => comparer(value, optionValue), | ||
); | ||
const valueIndex = findIndex( | ||
valuesClone, | ||
optionValue => comparer(value, optionValue), | ||
); | ||
|
||
if (valueIndex === -1) { | ||
return message; | ||
} | ||
}, | ||
if (valueIndex === -1) { | ||
return message; | ||
} | ||
}, | ||
|
||
field => `${field} must be one of ${JSON.stringify(valuesClone)}`, | ||
); | ||
} | ||
(field, values: Array<any>) => `${field} must be one of ${JSON.stringify(values.slice(0))}`, | ||
); |
Oops, something went wrong.