Skip to content

Commit

Permalink
feat: implement literal schema and rules
Browse files Browse the repository at this point in the history
  • Loading branch information
thetutlage committed Jun 7, 2023
1 parent e7f6a87 commit a2e60e5
Show file tree
Hide file tree
Showing 5 changed files with 239 additions and 13 deletions.
38 changes: 25 additions & 13 deletions src/schema/literal/rules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,36 @@
*/

import { helpers } from '../../vine/helpers.js'
import { errorMessages } from '../../defaults.js'
import { createRule } from '../../vine/create_rule.js'

/**
* Verifies two equals are equal considering the HTML forms
* serialization behavior.
*/
export const equalsRule = createRule<{ expectedValue: any }>((value, { expectedValue }, ctx) => {
if (typeof expectedValue === 'boolean') {
if (helpers.asBoolean(value) !== expectedValue) {
ctx.report(`Expected field value to be "${expectedValue}"`, 'equals', ctx)
}
} else if (typeof expectedValue === 'number') {
if (helpers.asNumber(value) !== expectedValue) {
ctx.report(`Expected field value to be "${expectedValue}"`, 'equals', ctx)
}
} else {
if (value !== expectedValue) {
ctx.report(`Expected field value to be "${expectedValue}"`, 'equals', ctx)
}
export const equalsRule = createRule<{ expectedValue: any }>((value, options, ctx) => {
let input = value

/**
* Normalizing the field value as per the expected
* value.
*/
if (typeof options.expectedValue === 'boolean') {
input = helpers.asBoolean(value)
} else if (typeof options.expectedValue === 'number') {
input = helpers.asNumber(value)
}

/**
* Performing validation and reporting error
*/
if (input !== options.expectedValue) {
ctx.report(errorMessages.literal, 'literal', ctx, options)
return
}

/**
* Mutating input with normalized value
*/
ctx.mutate(input, ctx)
})
48 changes: 48 additions & 0 deletions tests/integration/schema/literal.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* @vinejs/vine
*
* (c) VineJS
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

import { test } from '@japa/runner'
import vine from '../../../index.js'

test.group('Literal', () => {
test('fail when value is not same as the expected value', async ({ assert }) => {
const schema = vine.object({
is_hiring_guide: vine.literal(true),
})

const data = {
is_hiring_guide: false,
}

await assert.validationErrors(vine.validate({ schema, data }), [
{
field: 'is_hiring_guide',
message: 'The is_hiring_guide field must be true',
rule: 'literal',
meta: {
expectedValue: true,
},
},
])
})

test('pass when value is same as the expected value', async ({ assert }) => {
const schema = vine.object({
is_hiring_guide: vine.literal(true),
})

const data = {
is_hiring_guide: 'true',
}

await assert.validationOutput(vine.validate({ schema, data }), {
is_hiring_guide: true,
})
})
})
49 changes: 49 additions & 0 deletions tests/unit/rules/literal.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* @vinejs/vine
*
* (c) VineJS
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

import { test } from '@japa/runner'
import { validator } from '../../../factories/main.js'
import { equalsRule } from '../../../src/schema/literal/rules.js'

test.group('Literal | equals', () => {
test('report when input value is not same as expected value', () => {
const equals = equalsRule({ expectedValue: 1 })
const validated = validator.execute(equals, 'foo')

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

test('pass when input value is same as expected value', () => {
const equals = equalsRule({ expectedValue: 1 })
const validated = validator.execute(equals, 1)

validated.assertSucceeded()
validated.assertOutput(1)
})

test('normalize input value when expected value is a number', () => {
const equals = equalsRule({ expectedValue: 1 })
const validated = validator.execute(equals, '1')

validated.assertSucceeded()
validated.assertOutput(1)
})

test('normalize input value when expected value is a boolean', () => {
const equals = equalsRule({ expectedValue: false })
const validated = validator.execute(equals, 'false')
validated.assertSucceeded()
validated.assertOutput(false)

const equals1 = equalsRule({ expectedValue: true })
const validated1 = validator.execute(equals1, 'true')
validated1.assertSucceeded()
validated1.assertOutput(true)
})
})
59 changes: 59 additions & 0 deletions tests/unit/schema/literal.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,26 @@ test.group('VineLiteral', () => {
],
})
})

test('apply parser', ({ assert }) => {
const schema = vine.literal(22).parse(() => {})
assert.deepEqual(schema[PARSE]('*', refsBuilder(), { toCamelCase: false }), {
type: 'literal',
fieldName: '*',
propertyName: '*',
allowNull: false,
isOptional: false,
bail: true,
parseFnId: 'ref://1',
validations: [
{
implicit: false,
isAsync: false,
ruleFnId: 'ref://2',
},
],
})
})
})

test.group('VineLiteral | clone', () => {
Expand Down Expand Up @@ -313,4 +333,43 @@ test.group('VineLiteral | clone', () => {
],
})
})

test('clone and apply parser', ({ assert }) => {
const schema = vine.literal(22)
const schema1 = schema.clone().parse(() => {})

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

assert.deepEqual(schema1[PARSE]('*', refsBuilder(), { toCamelCase: false }), {
type: 'literal',
fieldName: '*',
propertyName: '*',
allowNull: false,
isOptional: false,
bail: true,
parseFnId: 'ref://1',
validations: [
{
implicit: false,
isAsync: false,
ruleFnId: 'ref://2',
},
],
})
})
})
58 changes: 58 additions & 0 deletions tests/unit/schema/native_enum.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,26 @@ test.group('VineNativeEnum', () => {
],
})
})

test('apply parser', ({ assert }) => {
const schema = vine.enum(Role).parse(() => {})
assert.deepEqual(schema[PARSE]('*', refsBuilder(), { toCamelCase: false }), {
type: 'literal',
fieldName: '*',
propertyName: '*',
allowNull: false,
isOptional: false,
bail: true,
parseFnId: 'ref://1',
validations: [
{
implicit: false,
isAsync: false,
ruleFnId: 'ref://2',
},
],
})
})
})

test.group('VineNativeEnum | clone', () => {
Expand Down Expand Up @@ -318,4 +338,42 @@ test.group('VineNativeEnum | clone', () => {
],
})
})

test('clone and apply parser', ({ assert }) => {
const schema = vine.enum(Role)
const schema1 = schema.clone().parse(() => {})

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

0 comments on commit a2e60e5

Please sign in to comment.