Skip to content

Commit

Permalink
feat: add in rule for number (#53)
Browse files Browse the repository at this point in the history
  • Loading branch information
FACON-Nicolas authored May 20, 2024
1 parent 34e07fc commit 72912af
Show file tree
Hide file tree
Showing 5 changed files with 96 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/defaults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export const messages = {
'boolean': 'The value must be a boolean',

'number': 'The {{ field }} field must be a number',
'number.in': 'The selected {{ field }} is not in {{ values }}',
'min': 'The {{ field }} field must be at least {{ min }}',
'max': 'The {{ field }} field must not be greater than {{ max }}',
'range': 'The {{ field }} field must be between {{ min }} and {{ max }}',
Expand Down
8 changes: 8 additions & 0 deletions src/schema/number/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
negativeRule,
positiveRule,
withoutDecimalsRule,
inRule,
} from './rules.js'

/**
Expand Down Expand Up @@ -121,4 +122,11 @@ export class VineNumber extends BaseLiteralType<string | number, number, number>
clone(): this {
return new VineNumber(this.cloneOptions(), this.cloneValidations()) as this
}

/**
* Enforce the value to be in a list of allowed values
*/
in(values: number[]) {
return this.use(inRule({ values }))
}
}
16 changes: 16 additions & 0 deletions src/schema/number/rules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,3 +147,19 @@ export const withoutDecimalsRule = createRule((value, _, field) => {
field.report(messages.withoutDecimals, 'withoutDecimals', field)
}
})

/**
* Enforce the value to be in a list of allowed values
*/
export const inRule = createRule<{ values: number[] }>((value, options, field) => {
/**
* Skip if the field is not valid.
*/
if (!field.isValid) {
return
}

if (!options.values.includes(value as number)) {
field.report(messages['number.in'], 'in', field, options)
}
})
37 changes: 37 additions & 0 deletions tests/unit/rules/number.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import { test } from '@japa/runner'
import { validator } from '../../../factories/main.js'
import {
inRule,
minRule,
maxRule,
rangeRule,
Expand Down Expand Up @@ -395,3 +396,39 @@ test.group('Number | withoutDecimals', () => {
validated.assertOutput(18)
})
})

test.group('Number | in', () => {
test('skip validation when value is not a number', () => {
const number = numberRule({})
const inArrayRule = inRule({ values: [1, 4] })
const validated = validator.execute([number, inArrayRule], 'foo')

validated.assertErrorsCount(1)
validated.assertError('The dummy field must be a number')
})

test('skip validation when value is not a number with bail mode disabled', () => {
const number = numberRule({})
const inArrayRule = inRule({ values: [1, 4] })
const validated = validator.bail(false).execute([number, inArrayRule], 'foo')

validated.assertErrorsCount(1)
validated.assertError('The dummy field must be a number')
})

test('work fine when value is in an array', () => {
const number = numberRule({})
const inArrayRule = inRule({ values: [1, 4] })
const validated = validator.execute([number, inArrayRule], 4)

validated.assertSucceeded()
})

test('fails when value is not in an array', () => {
const number = numberRule({})
const inArrayRule = inRule({ values: [1, 4] })
const validated = validator.execute([number, inArrayRule], 3)

validated.assertErrorsCount(1)
})
})
34 changes: 34 additions & 0 deletions tests/unit/schema/number.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { refsBuilder } from '@vinejs/compiler'
import { Vine } from '../../../src/vine/main.js'
import { IS_OF_TYPE, PARSE } from '../../../src/symbols.js'
import {
inRule,
maxRule,
minRule,
rangeRule,
Expand Down Expand Up @@ -743,4 +744,37 @@ test.group('VineNumber | applying rules', () => {
options: withoutDecimals.options,
})
})

test('apply in rule', ({ assert }) => {
const refs = refsBuilder()
const schema = vine.number().in([1, 2, 3])

assert.deepEqual(schema[PARSE]('*', refs, { toCamelCase: false }), {
type: 'literal',
fieldName: '*',
propertyName: '*',
bail: true,
allowNull: false,
isOptional: false,
parseFnId: undefined,
validations: [
{
implicit: false,
isAsync: false,
ruleFnId: 'ref://1',
},
{
implicit: false,
isAsync: false,
ruleFnId: 'ref://2',
},
],
})

const inArrayRule = inRule({ values: [1, 2, 3] })
assert.deepEqual(refs.toJSON()['ref://2'], {
validator: inArrayRule.rule.validator,
options: inArrayRule.options,
})
})
})

0 comments on commit 72912af

Please sign in to comment.