Skip to content

Commit

Permalink
feat: add tryValidate method
Browse files Browse the repository at this point in the history
  • Loading branch information
Harminder Virk authored and Harminder Virk committed Jun 2, 2024
1 parent 39204e4 commit cebb8e0
Show file tree
Hide file tree
Showing 2 changed files with 101 additions and 0 deletions.
23 changes: 23 additions & 0 deletions src/vine/validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import type { MessagesProviderContact, Refs, RootNode } from '@vinejs/compiler/t

import { messages } from '../defaults.js'
import { ITYPE, OTYPE, PARSE } from '../symbols.js'
import { ValidationError } from '../errors/validation_error.js'
import type {
Infer,
SchemaTypes,
Expand Down Expand Up @@ -158,6 +159,28 @@ export class VineValidator<
}
}

/**
* Performs validation without throwing the validation
* exception. Instead, the validation errors are
* returned as the first argument.
*/
async tryValidate(
data: any,
...[options]: [undefined] extends MetaData
? [options?: ValidationOptions<MetaData> | undefined]
: [options: ValidationOptions<MetaData>]
): Promise<[ValidationError, null] | [null, Infer<Schema>]> {
try {
const result = await this.validate(data, options!)
return [null, result]
} catch (error) {
if (error instanceof ValidationError) {
return [error, null]
}
throw error
}
}

/**
* Returns the compiled schema and refs.
*/
Expand Down
78 changes: 78 additions & 0 deletions tests/integration/validator.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import vine, {
VineLiteral,
VineBoolean,
} from '../../index.js'
import { Infer } from '../../src/types.js'
import { ValidationError } from '../../src/errors/validation_error.js'

test.group('Validator | metadata', () => {
test('pass metadata to the validation pipeline', async ({ assert }) => {
Expand Down Expand Up @@ -314,3 +316,79 @@ test.group('Validator | toJSON', () => {
`)
})
})

test.group('Validator | tryValidator', () => {
test('return validation errors without throwing an exception', async ({
assert,
expectTypeOf,
}) => {
const author = vine.object({
name: vine.string(),
email: vine.string().email(),
})

const validator = vine.compile(author)
const [error, result] = await validator.tryValidate({})
assert.instanceOf(error, ValidationError)
assert.isNull(result)

if (error) {
expectTypeOf(result).toMatchTypeOf(null)
expectTypeOf(error).toMatchTypeOf<ValidationError>()
}
if (result) {
expectTypeOf(error).toMatchTypeOf(null)
expectTypeOf(result).toMatchTypeOf<Infer<typeof validator>>()
}
})

test('rethrow non ValidationError errors', async () => {
const author = vine.object({
name: vine.string(),
email: vine.string().email(),
})

const validator = vine
.withMetaData<{ choices: string[] }>(() => {
throw new Error('Invalid metadata')
})
.compile(author)

await validator.tryValidate(
{},
{
meta: {
choices: [],
},
}
)
}).throws('Invalid metadata')

test('return validated data', async ({ assert, expectTypeOf }) => {
const author = vine.object({
name: vine.string(),
email: vine.string().email(),
})

const validator = vine.compile(author)
const [error, result] = await validator.tryValidate({
name: 'virk',
email: 'foo@bar.com',
})

assert.isNull(error)
assert.deepEqual(result, {
name: 'virk',
email: 'foo@bar.com',
})

if (error) {
expectTypeOf(result).toMatchTypeOf(null)
expectTypeOf(error).toMatchTypeOf<ValidationError>()
}
if (result) {
expectTypeOf(error).toMatchTypeOf(null)
expectTypeOf(result).toMatchTypeOf<Infer<typeof validator>>()
}
})
})

0 comments on commit cebb8e0

Please sign in to comment.