Skip to content

Commit

Permalink
vestResolver (#82)
Browse files Browse the repository at this point in the history
* update test and resolvers

* Update README.md

Co-authored-by: Joris <reixjoris@gmail.com>

* Update src/vest.ts

Co-authored-by: Joris <reixjoris@gmail.com>

* Update vest.ts

* Update README.md

Co-authored-by: Joris <reixjoris@gmail.com>

* Update vest.ts

* fix missing files with vest

* update package.json to include vest files

* Update src/vest.ts

Co-authored-by: Evyatar <code@ealush.com>

* fix readme

* Update src/vest.ts

Co-authored-by: Evyatar <code@ealush.com>

* Revert "Update src/vest.ts"

This reverts commit 1b2d12f.

* include promisfy

* exclude throw from promisfy

Co-authored-by: Joris <reixjoris@gmail.com>
Co-authored-by: Evyatar <code@ealush.com>
  • Loading branch information
3 people authored Dec 12, 2020
1 parent 634dab7 commit 5a868d4
Show file tree
Hide file tree
Showing 8 changed files with 243 additions and 3 deletions.
54 changes: 52 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@ const App = () => {
<form onSubmit={handleSubmit((d) => console.log(d))}>
<input name="name" ref={register} />
<input name="age" type="number" ref={register} />

<input type="submit" />
</form>
);
Expand Down Expand Up @@ -132,7 +131,6 @@ const App = () => {
<form onSubmit={handleSubmit((d) => console.log(d))}>
<input name="name" ref={register} />
<input name="age" type="number" ref={register} />

<input type="submit" />
</form>
);
Expand Down Expand Up @@ -164,7 +162,59 @@ const App = () => {
<form onSubmit={handleSubmit((d) => console.log(d))}>
<input name="name" ref={register} />
<input name="age" type="number" ref={register} />
<input type="submit" />
</form>
);
};
```

### [Vest](https://github.com/ealush/vest)

Vest 🦺 Declarative Validation Testing.

[![npm](https://img.shields.io/bundlephobia/minzip/vest?style=for-the-badge)](https://bundlephobia.com/result?p=vest)

```typescript jsx
import * as React from 'react';
import { useForm } from 'react-hook-form';
import { vestResolver } from '@hookform/resolvers/vest';
import vest, { test, enforce } from 'vest';

const validationSuite = vest.create((data = {}) => {
test('username', 'Username is required', () => {
enforce(data.username).isNotEmpty();
});

test('username', 'Must be longer than 3 chars', () => {
enforce(data.username).longerThan(3);
});

test('password', 'Password is required', () => {
enforce(data.password).isNotEmpty();
});

test('password', 'Password must be at least 5 chars', () => {
enforce(data.password).longerThanOrEquals(5);
});

test('password', 'Password must contain a digit', () => {
enforce(data.password).matches(/[0-9]/);
});

test('password', 'Password must contain a symbol', () => {
enforce(data.password).matches(/[^A-Za-z0-9]/);
});
});

const App = () => {
const { register, handleSubmit, errors } = useForm({
resolver: vestResolver(validationSuite),
});

return (
<form onSubmit={handleSubmit((data) => console.log(data))}>
<input type="text" name="username" ref={register} />
<input type="text" name="password" ref={register} />
<input type="submit" />
</form>
);
Expand Down
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@
"superstruct.js",
"superstruct.d.ts",
"zod.js",
"zod.d.ts"
"zod.d.ts",
"vest.js",
"vest.d.ts"
],
"publishConfig": {
"access": "public"
Expand Down Expand Up @@ -81,6 +83,7 @@
"ts-jest": "^26.4.4",
"typescript": "^4.1.2",
"yup": "^0.31.0",
"vest": "^2.2.3",
"zod": "^1.11.10"
},
"peerDependencies": {
Expand Down
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ export * from './yup';
export * from './superstruct';
export * from './joi';
export * from './zod';
export * from './vest';
120 changes: 120 additions & 0 deletions src/vest.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import * as vest from 'vest';
import { vestResolver } from './vest';

const validationSuite = vest.create('form', (data: any = {}) => {
vest.test('username', 'Username is required', () => {
vest.enforce(data.username).isNotEmpty();
});

vest.test('username', 'Must be longer than 3 chars', () => {
vest.enforce(data.username).longerThan(3);
});

vest.test('deepObject.data', 'deepObject.data is required', () => {
vest.enforce(data.deepObject.data).isNotEmpty();
});

vest.test('password', 'Password is required', () => {
vest.enforce(data.password).isNotEmpty();
});

vest.test('password', 'Password must be at least 5 chars', () => {
vest.enforce(data.password).longerThanOrEquals(5);
});

vest.test('password', 'Password must contain a digit', () => {
vest.enforce(data.password).matches(/[0-9]/);
});

vest.test('password', 'Password must contain a symbol', () => {
vest.enforce(data.password).matches(/[^A-Za-z0-9]/);
});
});

describe('vest', () => {
it('should return values from vestResolver when validation pass', async () => {
const data = {
username: 'asdda',
password: 'asddfg123!',
deepObject: {
data: 'test',
},
};
expect(await vestResolver(validationSuite)(data, {})).toEqual({
values: data,
errors: {},
});
});

it('should return single error message from vestResolver when validation fails and validateAllFieldCriteria set to false', async () => {
const data = {
username: '',
password: 'a',
deepObject: {
data: '',
},
};

expect(await vestResolver(validationSuite)(data, {})).toEqual({
values: {},
errors: {
username: {
type: '',
message: 'Username is required',
},
password: {
type: '',
message: 'Password must be at least 5 chars',
},
deepObject: {
data: {
type: '',
message: 'deepObject.data is required',
},
},
},
});
});

it('should return all the error messages from vestResolver when validation fails and validateAllFieldCriteria set to true', async () => {
const data = {
username: '',
password: 'a',
deepObject: {
data: '',
},
};

expect(await vestResolver(validationSuite, {}, true)(data, {})).toEqual({
values: {},
errors: {
username: {
type: '',
message: 'Username is required',
types: {
0: 'Username is required',
1: 'Must be longer than 3 chars',
},
},
password: {
type: '',
message: 'Password must be at least 5 chars',
types: {
0: 'Password must be at least 5 chars',
1: 'Password must contain a digit',
2: 'Password must contain a symbol',
},
},
deepObject: {
data: {
type: '',
message: 'deepObject.data is required',
types: {
0: 'deepObject.data is required',
},
},
},
},
});
});
});
59 changes: 59 additions & 0 deletions src/vest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { FieldValues, Resolver, transformToNestObject } from 'react-hook-form';
import * as Vest from 'vest';

type VestErrors = Record<string, string[]>;

type ICreateResult = ReturnType<typeof Vest.create>;

type Promisify = <T extends ICreateResult, K>(
fn: T,
) => (args: K) => Promise<Vest.IVestResult>;

const promisify: Promisify = (validatorFn) => (...args) =>
new Promise((resolve) => validatorFn(...args).done(resolve as Vest.DoneCB));

const parseErrorSchema = (
vestError: VestErrors,
validateAllFieldCriteria: boolean,
) => {
return Object.entries(vestError).reduce((prev, [key, value]) => {
return {
...prev,
[key]: {
type: '',
message: value[0],
...(validateAllFieldCriteria
? {
types: value.reduce((prev, message, index) => {
return {
...prev,
[index]: message,
};
}, {}),
}
: {}),
},
};
}, {});
};

export const vestResolver = <TFieldValues extends FieldValues>(
schema: ICreateResult,
_: any = {},
validateAllFieldCriteria = false,
): Resolver<TFieldValues> => async (values) => {
const validateSchema = promisify(schema);
const result = await validateSchema(values);
const errors = result.getErrors();

if (!result.hasErrors()) {
return { values: values as any, errors: {} };
}

return {
values: {},
errors: transformToNestObject(
parseErrorSchema(errors, validateAllFieldCriteria),
),
};
};
1 change: 1 addition & 0 deletions vest.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './dist/vest';
1 change: 1 addition & 0 deletions vest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = require('./dist/vest');
5 changes: 5 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4936,6 +4936,11 @@ verror@1.10.0:
core-util-is "1.0.2"
extsprintf "^1.2.0"

vest@^2.2.3:
version "2.2.3"
resolved "https://registry.yarnpkg.com/vest/-/vest-2.2.3.tgz#ff8a9bfc10d17b93b1b4e834da01fc6530634ac4"
integrity sha512-t1JMJFTmxHXbh+Y6gFc9iEKOfFehgura1Q7ASQearngKw/aCjgRfVHwP2bltzHCMhzsB+qMT5djh4EUMQy+JeQ==

w3c-hr-time@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz#0a89cdf5cc15822df9c360543676963e0cc308cd"
Expand Down

0 comments on commit 5a868d4

Please sign in to comment.