@hookform/resolvers 2.0.0-rc.1 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +115 -18
- package/dist/index.d.ts +1 -0
- package/dist/resolvers.js +2 -0
- package/dist/resolvers.js.map +1 -0
- package/dist/resolvers.mjs +2 -0
- package/dist/resolvers.modern.js +2 -0
- package/dist/resolvers.modern.js.map +1 -0
- package/dist/resolvers.module.js +2 -0
- package/dist/resolvers.module.js.map +1 -0
- package/dist/resolvers.umd.js +2 -0
- package/dist/resolvers.umd.js.map +1 -0
- package/dist/toNestError.d.ts +2 -0
- package/joi/dist/index.d.ts +2 -0
- package/joi/dist/joi.d.ts +2 -0
- package/joi/dist/joi.js +2 -0
- package/joi/dist/joi.js.map +1 -0
- package/joi/dist/joi.mjs +2 -0
- package/joi/dist/joi.modern.js +2 -0
- package/joi/dist/joi.modern.js.map +1 -0
- package/joi/dist/joi.module.js +2 -0
- package/joi/dist/joi.module.js.map +1 -0
- package/joi/dist/joi.umd.js +2 -0
- package/joi/dist/joi.umd.js.map +1 -0
- package/joi/dist/types.d.ts +5 -0
- package/joi/package.json +17 -0
- package/joi/src/__tests__/Form.tsx +61 -0
- package/joi/src/__tests__/__fixtures__/data.ts +76 -0
- package/joi/src/__tests__/__snapshots__/joi.ts.snap +283 -0
- package/joi/src/__tests__/joi.ts +89 -0
- package/joi/src/index.ts +2 -0
- package/joi/src/joi.ts +63 -0
- package/joi/src/types.ts +17 -0
- package/package.json +111 -55
- package/superstruct/dist/index.d.ts +2 -0
- package/superstruct/dist/superstruct.d.ts +2 -0
- package/superstruct/dist/superstruct.js +2 -0
- package/superstruct/dist/superstruct.js.map +1 -0
- package/superstruct/dist/superstruct.mjs +2 -0
- package/superstruct/dist/superstruct.modern.js +2 -0
- package/superstruct/dist/superstruct.modern.js.map +1 -0
- package/superstruct/dist/superstruct.module.js +2 -0
- package/superstruct/dist/superstruct.module.js.map +1 -0
- package/superstruct/dist/superstruct.umd.js +2 -0
- package/superstruct/dist/superstruct.umd.js.map +1 -0
- package/superstruct/dist/types.d.ts +5 -0
- package/superstruct/package.json +18 -0
- package/superstruct/src/__tests__/Form.tsx +62 -0
- package/superstruct/src/__tests__/__fixtures__/data.ts +73 -0
- package/superstruct/src/__tests__/__snapshots__/superstruct.ts.snap +64 -0
- package/superstruct/src/__tests__/superstruct.ts +20 -0
- package/superstruct/src/index.ts +2 -0
- package/superstruct/src/superstruct.ts +30 -0
- package/superstruct/src/types.ts +18 -0
- package/vest/dist/index.d.ts +1 -0
- package/vest/dist/vest.d.ts +2 -0
- package/vest/dist/vest.js +2 -0
- package/vest/dist/vest.js.map +1 -0
- package/vest/dist/vest.mjs +2 -0
- package/vest/dist/vest.modern.js +2 -0
- package/vest/dist/vest.modern.js.map +1 -0
- package/vest/dist/vest.module.js +2 -0
- package/vest/dist/vest.module.js.map +1 -0
- package/vest/dist/vest.umd.js +2 -0
- package/vest/dist/vest.umd.js.map +1 -0
- package/vest/package.json +18 -0
- package/vest/src/__tests__/Form.tsx +64 -0
- package/vest/src/__tests__/__fixtures__/data.ts +67 -0
- package/vest/src/__tests__/__snapshots__/vest.ts.snap +141 -0
- package/vest/src/__tests__/vest.ts +65 -0
- package/vest/src/index.ts +1 -0
- package/vest/src/types.ts +21 -0
- package/vest/src/vest.ts +45 -0
- package/yup/dist/index.d.ts +2 -0
- package/yup/dist/types.d.ts +8 -0
- package/yup/dist/yup.d.ts +2 -0
- package/yup/dist/yup.js +2 -0
- package/yup/dist/yup.js.map +1 -0
- package/yup/dist/yup.mjs +2 -0
- package/yup/dist/yup.modern.js +2 -0
- package/yup/dist/yup.modern.js.map +1 -0
- package/yup/dist/yup.module.js +2 -0
- package/yup/dist/yup.module.js.map +1 -0
- package/yup/dist/yup.umd.js +2 -0
- package/yup/dist/yup.umd.js.map +1 -0
- package/yup/package.json +17 -0
- package/yup/src/__tests__/Form.tsx +54 -0
- package/yup/src/__tests__/__fixtures__/data.ts +65 -0
- package/yup/src/__tests__/__snapshots__/yup.ts.snap +221 -0
- package/yup/src/__tests__/yup.ts +150 -0
- package/yup/src/index.ts +2 -0
- package/yup/src/types.ts +22 -0
- package/yup/src/yup.ts +65 -0
- package/zod/dist/index.d.ts +2 -0
- package/zod/dist/types.d.ts +6 -0
- package/zod/dist/zod.d.ts +2 -0
- package/zod/dist/zod.js +2 -0
- package/zod/dist/zod.js.map +1 -0
- package/zod/dist/zod.mjs +2 -0
- package/zod/dist/zod.modern.js +2 -0
- package/zod/dist/zod.modern.js.map +1 -0
- package/zod/dist/zod.module.js +2 -0
- package/zod/dist/zod.module.js.map +1 -0
- package/zod/dist/zod.umd.js +2 -0
- package/zod/dist/zod.umd.js.map +1 -0
- package/zod/package.json +17 -0
- package/zod/src/__tests__/Form.tsx +54 -0
- package/zod/src/__tests__/__fixtures__/data.ts +68 -0
- package/zod/src/__tests__/__snapshots__/zod.ts.snap +355 -0
- package/zod/src/__tests__/zod.ts +71 -0
- package/zod/src/index.ts +2 -0
- package/zod/src/types.ts +18 -0
- package/zod/src/zod.ts +65 -0
- package/dist/ie11/joi.d.ts +0 -3
- package/dist/ie11/joi.js +0 -104
- package/dist/ie11/joi.js.map +0 -1
- package/dist/ie11/superstruct.d.ts +0 -3
- package/dist/ie11/superstruct.js +0 -95
- package/dist/ie11/superstruct.js.map +0 -1
- package/dist/ie11/utils/convertArrayToPathName.d.ts +0 -2
- package/dist/ie11/utils/convertArrayToPathName.js +0 -12
- package/dist/ie11/utils/convertArrayToPathName.js.map +0 -1
- package/dist/ie11/yup.d.ts +0 -3
- package/dist/ie11/yup.js +0 -132
- package/dist/ie11/yup.js.map +0 -1
- package/dist/joi.d.ts +0 -3
- package/dist/joi.js +0 -41
- package/dist/joi.js.map +0 -1
- package/dist/superstruct.d.ts +0 -3
- package/dist/superstruct.js +0 -39
- package/dist/superstruct.js.map +0 -1
- package/dist/umd/index.js +0 -2
- package/dist/umd/index.js.map +0 -1
- package/dist/utils/convertArrayToPathName.d.ts +0 -2
- package/dist/utils/convertArrayToPathName.js +0 -8
- package/dist/utils/convertArrayToPathName.js.map +0 -1
- package/dist/yup.d.ts +0 -3
- package/dist/yup.js +0 -49
- package/dist/yup.js.map +0 -1
- package/joi.d.ts +0 -1
- package/joi.js +0 -1
- package/superstruct.js +0 -1
- package/yup.d.ts +0 -1
- package/yup.js +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"superstruct.js","sources":["../src/superstruct.ts"],"sourcesContent":["import { FieldError } from 'react-hook-form';\nimport { toNestError } from '@hookform/resolvers';\n\nimport { StructError, validate } from 'superstruct';\nimport { Resolver } from './types';\n\nconst parseErrorSchema = (error: StructError) =>\n error.failures().reduce<Record<string, FieldError>>(\n (previous, error) =>\n (previous[error.path.join('.')] = {\n message: error.message,\n type: error.type,\n }) && previous,\n {},\n );\n\nexport const superstructResolver: Resolver = (schema, resolverOptions) => (\n values,\n _,\n options,\n) => {\n const result = validate(values, schema, resolverOptions);\n\n return {\n values: result[1] || {},\n errors: result[0]\n ? toNestError(parseErrorSchema(result[0]), options.fields)\n : {},\n };\n};\n"],"names":["schema","resolverOptions","values","_","options","error","result","validate","errors","toNestError","failures","reduce","previous","path","join","message","type","fields"],"mappings":"0FAgB6C,SAACA,EAAQC,mBACpDC,EACAC,EACAC,GAEA,IAfwBC,EAelBC,EAASC,WAASL,EAAQF,EAAQC,GAExC,MAAO,CACLC,OAAQI,EAAO,IAAM,GACrBE,OAAQF,EAAO,GACXG,eApBkBJ,EAoBWC,EAAO,GAnB1CD,EAAMK,WAAWC,OACf,SAACC,EAAUP,UACRO,EAASP,EAAMQ,KAAKC,KAAK,MAAQ,CAChCC,QAASV,EAAMU,QACfC,KAAMX,EAAMW,QACRJ,GACR,KAa6CR,EAAQa,QACjD"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{toNestError as r}from"@hookform/resolvers";import{validate as e}from"superstruct";var o=function(o,t){return function(s,u,n){var f,i=e(s,o,t);return{values:i[1]||{},errors:i[0]?r((f=i[0],f.failures().reduce(function(r,e){return(r[e.path.join(".")]={message:e.message,type:e.type})&&r},{})),n.fields):{}}}};export{o as superstructResolver};
|
|
2
|
+
//# sourceMappingURL=superstruct.module.js.map
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{toNestError as r}from"@hookform/resolvers";import{validate as e}from"superstruct";const s=(s,o)=>(t,m,p)=>{const a=e(t,s,o);return{values:a[1]||{},errors:a[0]?r((u=a[0],u.failures().reduce((r,e)=>(r[e.path.join(".")]={message:e.message,type:e.type})&&r,{})),p.fields):{}};var u};export{s as superstructResolver};
|
|
2
|
+
//# sourceMappingURL=superstruct.modern.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"superstruct.modern.js","sources":["../src/superstruct.ts"],"sourcesContent":["import { FieldError } from 'react-hook-form';\nimport { toNestError } from '@hookform/resolvers';\n\nimport { StructError, validate } from 'superstruct';\nimport { Resolver } from './types';\n\nconst parseErrorSchema = (error: StructError) =>\n error.failures().reduce<Record<string, FieldError>>(\n (previous, error) =>\n (previous[error.path.join('.')] = {\n message: error.message,\n type: error.type,\n }) && previous,\n {},\n );\n\nexport const superstructResolver: Resolver = (schema, resolverOptions) => (\n values,\n _,\n options,\n) => {\n const result = validate(values, schema, resolverOptions);\n\n return {\n values: result[1] || {},\n errors: result[0]\n ? toNestError(parseErrorSchema(result[0]), options.fields)\n : {},\n };\n};\n"],"names":["superstructResolver","schema","resolverOptions","values","_","options","result","validate","errors","toNestError","error","failures","reduce","previous","path","join","message","type","fields"],"mappings":"yFAMA,MAUaA,EAAgC,CAACC,EAAQC,IAAoB,CACxEC,EACAC,EACAC,KAEA,MAAMC,EAASC,EAASJ,EAAQF,EAAQC,GAExC,MAAO,CACLC,OAAQG,EAAO,IAAM,GACrBE,OAAQF,EAAO,GACXG,GApBkBC,EAoBWJ,EAAO,GAnB1CI,EAAMC,WAAWC,OACf,CAACC,EAAUH,KACRG,EAASH,EAAMI,KAAKC,KAAK,MAAQ,CAChCC,QAASN,EAAMM,QACfC,KAAMP,EAAMO,QACRJ,EACR,KAa6CR,EAAQa,QACjD,IArBkBR,IAAAA"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{toNestError as r}from"@hookform/resolvers";import{validate as e}from"superstruct";var o=function(o,t){return function(s,u,n){var f,i=e(s,o,t);return{values:i[1]||{},errors:i[0]?r((f=i[0],f.failures().reduce(function(r,e){return(r[e.path.join(".")]={message:e.message,type:e.type})&&r},{})),n.fields):{}}}};export{o as superstructResolver};
|
|
2
|
+
//# sourceMappingURL=superstruct.module.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"superstruct.module.js","sources":["../src/superstruct.ts"],"sourcesContent":["import { FieldError } from 'react-hook-form';\nimport { toNestError } from '@hookform/resolvers';\n\nimport { StructError, validate } from 'superstruct';\nimport { Resolver } from './types';\n\nconst parseErrorSchema = (error: StructError) =>\n error.failures().reduce<Record<string, FieldError>>(\n (previous, error) =>\n (previous[error.path.join('.')] = {\n message: error.message,\n type: error.type,\n }) && previous,\n {},\n );\n\nexport const superstructResolver: Resolver = (schema, resolverOptions) => (\n values,\n _,\n options,\n) => {\n const result = validate(values, schema, resolverOptions);\n\n return {\n values: result[1] || {},\n errors: result[0]\n ? toNestError(parseErrorSchema(result[0]), options.fields)\n : {},\n };\n};\n"],"names":["superstructResolver","schema","resolverOptions","values","_","options","error","result","validate","errors","toNestError","failures","reduce","previous","path","join","message","type","fields"],"mappings":"yFAMA,IAUaA,EAAgC,SAACC,EAAQC,mBACpDC,EACAC,EACAC,GAEA,IAfwBC,EAelBC,EAASC,EAASL,EAAQF,EAAQC,GAExC,MAAO,CACLC,OAAQI,EAAO,IAAM,GACrBE,OAAQF,EAAO,GACXG,GApBkBJ,EAoBWC,EAAO,GAnB1CD,EAAMK,WAAWC,OACf,SAACC,EAAUP,UACRO,EAASP,EAAMQ,KAAKC,KAAK,MAAQ,CAChCC,QAASV,EAAMU,QACfC,KAAMX,EAAMW,QACRJ,GACR,KAa6CR,EAAQa,QACjD"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
!function(e,r){"object"==typeof exports&&"undefined"!=typeof module?r(exports,require("@hookform/resolvers"),require("superstruct")):"function"==typeof define&&define.amd?define(["exports","@hookform/resolvers","superstruct"],r):r((e||self).hookformResolversSuperstruct={},e.hookformResolvers,e.superstruct)}(this,function(e,r,o){e.superstructResolver=function(e,s){return function(t,u,n){var f,i=o.validate(t,e,s);return{values:i[1]||{},errors:i[0]?r.toNestError((f=i[0],f.failures().reduce(function(e,r){return(e[r.path.join(".")]={message:r.message,type:r.type})&&e},{})),n.fields):{}}}}});
|
|
2
|
+
//# sourceMappingURL=superstruct.umd.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"superstruct.umd.js","sources":["../src/superstruct.ts"],"sourcesContent":["import { FieldError } from 'react-hook-form';\nimport { toNestError } from '@hookform/resolvers';\n\nimport { StructError, validate } from 'superstruct';\nimport { Resolver } from './types';\n\nconst parseErrorSchema = (error: StructError) =>\n error.failures().reduce<Record<string, FieldError>>(\n (previous, error) =>\n (previous[error.path.join('.')] = {\n message: error.message,\n type: error.type,\n }) && previous,\n {},\n );\n\nexport const superstructResolver: Resolver = (schema, resolverOptions) => (\n values,\n _,\n options,\n) => {\n const result = validate(values, schema, resolverOptions);\n\n return {\n values: result[1] || {},\n errors: result[0]\n ? toNestError(parseErrorSchema(result[0]), options.fields)\n : {},\n };\n};\n"],"names":["schema","resolverOptions","values","_","options","error","result","validate","errors","toNestError","failures","reduce","previous","path","join","message","type","fields"],"mappings":"4YAgB6C,SAACA,EAAQC,mBACpDC,EACAC,EACAC,GAEA,IAfwBC,EAelBC,EAASC,WAASL,EAAQF,EAAQC,GAExC,MAAO,CACLC,OAAQI,EAAO,IAAM,GACrBE,OAAQF,EAAO,GACXG,eApBkBJ,EAoBWC,EAAO,GAnB1CD,EAAMK,WAAWC,OACf,SAACC,EAAUP,UACRO,EAASP,EAAMQ,KAAKC,KAAK,MAAQ,CAChCC,QAASV,EAAMU,QACfC,KAAMX,EAAMW,QACRJ,GACR,KAa6CR,EAAQa,QACjD"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { FieldValues, ResolverOptions, ResolverResult, UnpackNestedValue } from 'react-hook-form';
|
|
2
|
+
import { validate, Struct } from 'superstruct';
|
|
3
|
+
declare type Options = Parameters<typeof validate>[2];
|
|
4
|
+
export declare type Resolver = <T extends Struct<any, any>>(schema: T, factoryOtions?: Options) => <TFieldValues extends FieldValues, TContext>(values: UnpackNestedValue<TFieldValues>, context: TContext | undefined, options: ResolverOptions<TFieldValues>) => ResolverResult<TFieldValues>;
|
|
5
|
+
export {};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "superstruct",
|
|
3
|
+
"amdName": "hookformResolversSuperstruct",
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"private": true,
|
|
6
|
+
"description": "React Hook Form validation resolver: superstruct",
|
|
7
|
+
"main": "dist/superstruct.js",
|
|
8
|
+
"module": "dist/superstruct.module.js",
|
|
9
|
+
"umd:main": "dist/superstruct.umd.js",
|
|
10
|
+
"source": "src/index.ts",
|
|
11
|
+
"types": "dist/index.d.ts",
|
|
12
|
+
"license": "MIT",
|
|
13
|
+
"peerDependencies": {
|
|
14
|
+
"react-hook-form": ">=6.6.0",
|
|
15
|
+
"@hookform/resolvers": "^1.0.0",
|
|
16
|
+
"superstruct": ">=0.12.0"
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render, screen, act } from '@testing-library/react';
|
|
3
|
+
import user from '@testing-library/user-event';
|
|
4
|
+
import { useForm } from 'react-hook-form';
|
|
5
|
+
import { object, string, Infer, size } from 'superstruct';
|
|
6
|
+
import { superstructResolver } from '..';
|
|
7
|
+
|
|
8
|
+
const schema = object({
|
|
9
|
+
username: size(string(), 2),
|
|
10
|
+
password: size(string(), 6),
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
type FormData = Infer<typeof schema>;
|
|
14
|
+
|
|
15
|
+
interface Props {
|
|
16
|
+
onSubmit: (data: FormData) => void;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function TestComponent({ onSubmit }: Props) {
|
|
20
|
+
const {
|
|
21
|
+
register,
|
|
22
|
+
formState: { errors },
|
|
23
|
+
handleSubmit,
|
|
24
|
+
} = useForm<FormData>({
|
|
25
|
+
resolver: superstructResolver(schema), // Useful to check TypeScript regressions
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<form onSubmit={handleSubmit(onSubmit)}>
|
|
30
|
+
<input {...register('username')} />
|
|
31
|
+
{errors.username && <span role="alert">{errors.username.message}</span>}
|
|
32
|
+
|
|
33
|
+
<input {...register('password')} />
|
|
34
|
+
{errors.password && <span role="alert">{errors.password.message}</span>}
|
|
35
|
+
|
|
36
|
+
<button type="submit">submit</button>
|
|
37
|
+
</form>
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
test("form's validation with Superstruct and TypeScript's integration", async () => {
|
|
42
|
+
const handleSubmit = jest.fn();
|
|
43
|
+
render(<TestComponent onSubmit={handleSubmit} />);
|
|
44
|
+
|
|
45
|
+
expect(screen.queryAllByRole(/alert/i)).toHaveLength(0);
|
|
46
|
+
|
|
47
|
+
await act(async () => {
|
|
48
|
+
user.click(screen.getByText(/submit/i));
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
expect(
|
|
52
|
+
screen.getByText(
|
|
53
|
+
/Expected a string with a length of `2` but received one with a length of `0`/i,
|
|
54
|
+
),
|
|
55
|
+
).toBeInTheDocument();
|
|
56
|
+
expect(
|
|
57
|
+
screen.getByText(
|
|
58
|
+
/Expected a string with a length of `6` but received one with a length of `0`/i,
|
|
59
|
+
),
|
|
60
|
+
).toBeInTheDocument();
|
|
61
|
+
expect(handleSubmit).not.toHaveBeenCalled();
|
|
62
|
+
});
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { Field, InternalFieldName } from 'react-hook-form';
|
|
2
|
+
import {
|
|
3
|
+
object,
|
|
4
|
+
number,
|
|
5
|
+
string,
|
|
6
|
+
optional,
|
|
7
|
+
pattern,
|
|
8
|
+
size,
|
|
9
|
+
union,
|
|
10
|
+
min,
|
|
11
|
+
max,
|
|
12
|
+
Infer,
|
|
13
|
+
define,
|
|
14
|
+
array,
|
|
15
|
+
boolean,
|
|
16
|
+
} from 'superstruct';
|
|
17
|
+
|
|
18
|
+
const Password = define('Password', (value, ctx) =>
|
|
19
|
+
value === ctx.branch[0].password);
|
|
20
|
+
|
|
21
|
+
export const schema = object({
|
|
22
|
+
username: size(pattern(string(), /^\w+$/), 3, 30),
|
|
23
|
+
password: pattern(string(), /^[a-zA-Z0-9]{3,30}/),
|
|
24
|
+
repeatPassword: Password,
|
|
25
|
+
accessToken: optional(union([string(), number()])),
|
|
26
|
+
birthYear: optional(max(min(number(), 1900), 2013)),
|
|
27
|
+
email: optional(pattern(string(), /^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$/)),
|
|
28
|
+
tags: array(string()),
|
|
29
|
+
enabled: boolean(),
|
|
30
|
+
like: optional(array(object({ id: number(), name: size(string(), 4) }))),
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
export const validData: Infer<typeof schema> = {
|
|
34
|
+
username: 'Doe',
|
|
35
|
+
password: 'Password123',
|
|
36
|
+
repeatPassword: 'Password123',
|
|
37
|
+
birthYear: 2000,
|
|
38
|
+
email: 'john@doe.com',
|
|
39
|
+
tags: ['tag1', 'tag2'],
|
|
40
|
+
enabled: true,
|
|
41
|
+
like: [
|
|
42
|
+
{
|
|
43
|
+
id: 1,
|
|
44
|
+
name: 'name',
|
|
45
|
+
},
|
|
46
|
+
],
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export const invalidData = {
|
|
50
|
+
password: '___',
|
|
51
|
+
email: '',
|
|
52
|
+
birthYear: 'birthYear',
|
|
53
|
+
like: [{ id: 'z' }],
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export const fields: Record<InternalFieldName, Field['_f']> = {
|
|
57
|
+
username: {
|
|
58
|
+
ref: { name: 'username' },
|
|
59
|
+
name: 'username',
|
|
60
|
+
},
|
|
61
|
+
password: {
|
|
62
|
+
ref: { name: 'password' },
|
|
63
|
+
name: 'password',
|
|
64
|
+
},
|
|
65
|
+
email: {
|
|
66
|
+
ref: { name: 'email' },
|
|
67
|
+
name: 'email',
|
|
68
|
+
},
|
|
69
|
+
birthday: {
|
|
70
|
+
ref: { name: 'birthday' },
|
|
71
|
+
name: 'birthday',
|
|
72
|
+
},
|
|
73
|
+
};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
|
+
|
|
3
|
+
exports[`superstructResolver should return a single error from superstructResolver when validation fails 1`] = `
|
|
4
|
+
Object {
|
|
5
|
+
"errors": Object {
|
|
6
|
+
"birthYear": Object {
|
|
7
|
+
"message": "Expected a number, but received: \\"birthYear\\"",
|
|
8
|
+
"ref": undefined,
|
|
9
|
+
"type": "number",
|
|
10
|
+
},
|
|
11
|
+
"email": Object {
|
|
12
|
+
"message": "Expected a string matching \`/^[\\\\w-\\\\.]+@([\\\\w-]+\\\\.)+[\\\\w-]{2,4}$/\` but received \\"\\"",
|
|
13
|
+
"ref": Object {
|
|
14
|
+
"name": "email",
|
|
15
|
+
},
|
|
16
|
+
"type": "string",
|
|
17
|
+
},
|
|
18
|
+
"enabled": Object {
|
|
19
|
+
"message": "Expected a value of type \`boolean\`, but received: \`undefined\`",
|
|
20
|
+
"ref": undefined,
|
|
21
|
+
"type": "boolean",
|
|
22
|
+
},
|
|
23
|
+
"like": Array [
|
|
24
|
+
Object {
|
|
25
|
+
"id": Object {
|
|
26
|
+
"message": "Expected a number, but received: \\"z\\"",
|
|
27
|
+
"ref": undefined,
|
|
28
|
+
"type": "number",
|
|
29
|
+
},
|
|
30
|
+
"name": Object {
|
|
31
|
+
"message": "Expected a string, but received: undefined",
|
|
32
|
+
"ref": undefined,
|
|
33
|
+
"type": "string",
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
],
|
|
37
|
+
"password": Object {
|
|
38
|
+
"message": "Expected a string matching \`/^[a-zA-Z0-9]{3,30}/\` but received \\"___\\"",
|
|
39
|
+
"ref": Object {
|
|
40
|
+
"name": "password",
|
|
41
|
+
},
|
|
42
|
+
"type": "string",
|
|
43
|
+
},
|
|
44
|
+
"repeatPassword": Object {
|
|
45
|
+
"message": "Expected a value of type \`Password\`, but received: \`undefined\`",
|
|
46
|
+
"ref": undefined,
|
|
47
|
+
"type": "Password",
|
|
48
|
+
},
|
|
49
|
+
"tags": Object {
|
|
50
|
+
"message": "Expected an array value, but received: undefined",
|
|
51
|
+
"ref": undefined,
|
|
52
|
+
"type": "array",
|
|
53
|
+
},
|
|
54
|
+
"username": Object {
|
|
55
|
+
"message": "Expected a string, but received: undefined",
|
|
56
|
+
"ref": Object {
|
|
57
|
+
"name": "username",
|
|
58
|
+
},
|
|
59
|
+
"type": "string",
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
"values": Object {},
|
|
63
|
+
}
|
|
64
|
+
`;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { superstructResolver } from '..';
|
|
2
|
+
import { invalidData, schema, validData, fields } from './__fixtures__/data';
|
|
3
|
+
|
|
4
|
+
describe('superstructResolver', () => {
|
|
5
|
+
it('should return values from superstructResolver when validation pass', async () => {
|
|
6
|
+
const result = await superstructResolver(schema)(validData, undefined, {
|
|
7
|
+
fields,
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
expect(result).toEqual({ errors: {}, values: validData });
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it('should return a single error from superstructResolver when validation fails', async () => {
|
|
14
|
+
const result = await superstructResolver(schema)(invalidData, undefined, {
|
|
15
|
+
fields,
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
expect(result).toMatchSnapshot();
|
|
19
|
+
});
|
|
20
|
+
});
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { FieldError } from 'react-hook-form';
|
|
2
|
+
import { toNestError } from '@hookform/resolvers';
|
|
3
|
+
|
|
4
|
+
import { StructError, validate } from 'superstruct';
|
|
5
|
+
import { Resolver } from './types';
|
|
6
|
+
|
|
7
|
+
const parseErrorSchema = (error: StructError) =>
|
|
8
|
+
error.failures().reduce<Record<string, FieldError>>(
|
|
9
|
+
(previous, error) =>
|
|
10
|
+
(previous[error.path.join('.')] = {
|
|
11
|
+
message: error.message,
|
|
12
|
+
type: error.type,
|
|
13
|
+
}) && previous,
|
|
14
|
+
{},
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
export const superstructResolver: Resolver = (schema, resolverOptions) => (
|
|
18
|
+
values,
|
|
19
|
+
_,
|
|
20
|
+
options,
|
|
21
|
+
) => {
|
|
22
|
+
const result = validate(values, schema, resolverOptions);
|
|
23
|
+
|
|
24
|
+
return {
|
|
25
|
+
values: result[1] || {},
|
|
26
|
+
errors: result[0]
|
|
27
|
+
? toNestError(parseErrorSchema(result[0]), options.fields)
|
|
28
|
+
: {},
|
|
29
|
+
};
|
|
30
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import {
|
|
2
|
+
FieldValues,
|
|
3
|
+
ResolverOptions,
|
|
4
|
+
ResolverResult,
|
|
5
|
+
UnpackNestedValue,
|
|
6
|
+
} from 'react-hook-form';
|
|
7
|
+
import { validate, Struct } from 'superstruct';
|
|
8
|
+
|
|
9
|
+
type Options = Parameters<typeof validate>[2];
|
|
10
|
+
|
|
11
|
+
export type Resolver = <T extends Struct<any, any>>(
|
|
12
|
+
schema: T,
|
|
13
|
+
factoryOtions?: Options,
|
|
14
|
+
) => <TFieldValues extends FieldValues, TContext>(
|
|
15
|
+
values: UnpackNestedValue<TFieldValues>,
|
|
16
|
+
context: TContext | undefined,
|
|
17
|
+
options: ResolverOptions<TFieldValues>,
|
|
18
|
+
) => ResolverResult<TFieldValues>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './vest';
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var r=require("@hookform/resolvers");function e(r){return r&&"object"==typeof r&&"default"in r?r:{default:r}}var t=e(require("vest/promisify")),o=function(r,e){var t={};for(var o in r)t[o]||(t[o]={message:r[o][0],type:""}),e&&(t[o].types=r[o].reduce(function(r,e,t){return(r[t]=e)&&r},{}));return t};exports.vestResolver=function(e,s,n){return void 0===n&&(n={}),function(s,u,i){try{var a=function(e){return e.hasErrors()?{values:{},errors:r.toNestError(o(e.getErrors(),"all"===i.criteriaMode),i.fields)}:{values:s,errors:{}}};return Promise.resolve("sync"===n.mode?a(e(s)):Promise.resolve(t.default(e)(s)).then(a))}catch(r){return Promise.reject(r)}}};
|
|
2
|
+
//# sourceMappingURL=vest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vest.js","sources":["../src/vest.ts"],"sourcesContent":["import { toNestError } from '@hookform/resolvers';\nimport { FieldError } from 'react-hook-form';\nimport promisify from 'vest/promisify';\nimport type { VestErrors, Resolver } from './types';\n\nconst parseErrorSchema = (\n vestError: VestErrors,\n validateAllFieldCriteria: boolean,\n) => {\n const errors: Record<string, FieldError> = {};\n for (const path in vestError) {\n if (!errors[path]) {\n errors[path] = { message: vestError[path][0], type: '' };\n }\n\n if (validateAllFieldCriteria) {\n errors[path].types = vestError[path].reduce<Record<number, string>>(\n (acc, message, index) => (acc[index] = message) && acc,\n {},\n );\n }\n }\n return errors;\n};\n\nexport const vestResolver: Resolver = (\n schema,\n _,\n resolverOptions = {},\n) => async (values, _context, options) => {\n const result =\n resolverOptions.mode === 'sync'\n ? schema(values)\n : await promisify(schema)(values);\n\n return result.hasErrors()\n ? {\n values: {},\n errors: toNestError(\n parseErrorSchema(result.getErrors(), options.criteriaMode === 'all'),\n options.fields,\n ),\n }\n : { values, errors: {} };\n};\n"],"names":["parseErrorSchema","vestError","validateAllFieldCriteria","errors","path","message","type","types","reduce","acc","index","schema","_","resolverOptions","values","_context","options","result","hasErrors","toNestError","getErrors","criteriaMode","fields","mode","promisify"],"mappings":"gJAKMA,EAAmB,SACvBC,EACAC,GAEA,IAAMC,EAAqC,GAC3C,IAAK,IAAMC,KAAQH,EACZE,EAAOC,KACVD,EAAOC,GAAQ,CAAEC,QAASJ,EAAUG,GAAM,GAAIE,KAAM,KAGlDJ,IACFC,EAAOC,GAAMG,MAAQN,EAAUG,GAAMI,OACnC,SAACC,EAAKJ,EAASK,UAAWD,EAAIC,GAASL,IAAYI,GACnD,KAIN,OAAON,wBAG6B,SACpCQ,EACAC,EACAC,mBAAAA,IAAAA,EAAkB,aACRC,EAAQC,EAAUC,sBACtBC,GAKN,OAAOA,EAAOC,YACV,CACEJ,OAAQ,GACRX,OAAQgB,cACNnB,EAAiBiB,EAAOG,YAAsC,QAAzBJ,EAAQK,cAC7CL,EAAQM,SAGZ,CAAER,OAAAA,EAAQX,OAAQ,4BAZK,SAAzBU,EAAgBU,OACZZ,EAAOG,oBACDU,UAAUb,EAAVa,CAAkBV,aARM"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{toNestError as r}from"@hookform/resolvers";import e from"vest/promisify";var o=function(r,e){var o={};for(var t in r)o[t]||(o[t]={message:r[t][0],type:""}),e&&(o[t].types=r[t].reduce(function(r,e,o){return(r[o]=e)&&r},{}));return o},t=function(t,s,n){return void 0===n&&(n={}),function(s,i,u){try{var a=function(e){return e.hasErrors()?{values:{},errors:r(o(e.getErrors(),"all"===u.criteriaMode),u.fields)}:{values:s,errors:{}}};return Promise.resolve("sync"===n.mode?a(t(s)):Promise.resolve(e(t)(s)).then(a))}catch(r){return Promise.reject(r)}}};export{t as vestResolver};
|
|
2
|
+
//# sourceMappingURL=vest.module.js.map
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{toNestError as r}from"@hookform/resolvers";import e from"vest/promisify";const o=(r,e)=>{const o={};for(const s in r)o[s]||(o[s]={message:r[s][0],type:""}),e&&(o[s].types=r[s].reduce((r,e,o)=>(r[o]=e)&&r,{}));return o},s=(s,t,a={})=>async(t,i,n)=>{const c="sync"===a.mode?s(t):await e(s)(t);return c.hasErrors()?{values:{},errors:r(o(c.getErrors(),"all"===n.criteriaMode),n.fields)}:{values:t,errors:{}}};export{s as vestResolver};
|
|
2
|
+
//# sourceMappingURL=vest.modern.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vest.modern.js","sources":["../src/vest.ts"],"sourcesContent":["import { toNestError } from '@hookform/resolvers';\nimport { FieldError } from 'react-hook-form';\nimport promisify from 'vest/promisify';\nimport type { VestErrors, Resolver } from './types';\n\nconst parseErrorSchema = (\n vestError: VestErrors,\n validateAllFieldCriteria: boolean,\n) => {\n const errors: Record<string, FieldError> = {};\n for (const path in vestError) {\n if (!errors[path]) {\n errors[path] = { message: vestError[path][0], type: '' };\n }\n\n if (validateAllFieldCriteria) {\n errors[path].types = vestError[path].reduce<Record<number, string>>(\n (acc, message, index) => (acc[index] = message) && acc,\n {},\n );\n }\n }\n return errors;\n};\n\nexport const vestResolver: Resolver = (\n schema,\n _,\n resolverOptions = {},\n) => async (values, _context, options) => {\n const result =\n resolverOptions.mode === 'sync'\n ? schema(values)\n : await promisify(schema)(values);\n\n return result.hasErrors()\n ? {\n values: {},\n errors: toNestError(\n parseErrorSchema(result.getErrors(), options.criteriaMode === 'all'),\n options.fields,\n ),\n }\n : { values, errors: {} };\n};\n"],"names":["parseErrorSchema","vestError","validateAllFieldCriteria","errors","path","message","type","types","reduce","acc","index","vestResolver","schema","_","resolverOptions","async","values","_context","options","result","mode","promisify","hasErrors","toNestError","getErrors","criteriaMode","fields"],"mappings":"gFAKA,MAAMA,EAAmB,CACvBC,EACAC,KAEA,MAAMC,EAAqC,GAC3C,IAAK,MAAMC,KAAQH,EACZE,EAAOC,KACVD,EAAOC,GAAQ,CAAEC,QAASJ,EAAUG,GAAM,GAAIE,KAAM,KAGlDJ,IACFC,EAAOC,GAAMG,MAAQN,EAAUG,GAAMI,OACnC,CAACC,EAAKJ,EAASK,KAAWD,EAAIC,GAASL,IAAYI,EACnD,KAIN,OAAON,GAGIQ,EAAyB,CACpCC,EACAC,EACAC,EAAkB,KACfC,MAAOC,EAAQC,EAAUC,KAC5B,MAAMC,EACqB,SAAzBL,EAAgBM,KACZR,EAAOI,SACDK,EAAUT,EAAVS,CAAkBL,GAE9B,OAAOG,EAAOG,YACV,CACEN,OAAQ,GACRb,OAAQoB,EACNvB,EAAiBmB,EAAOK,YAAsC,QAAzBN,EAAQO,cAC7CP,EAAQQ,SAGZ,CAAEV,OAAAA,EAAQb,OAAQ"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{toNestError as r}from"@hookform/resolvers";import e from"vest/promisify";var o=function(r,e){var o={};for(var t in r)o[t]||(o[t]={message:r[t][0],type:""}),e&&(o[t].types=r[t].reduce(function(r,e,o){return(r[o]=e)&&r},{}));return o},t=function(t,s,n){return void 0===n&&(n={}),function(s,i,u){try{var a=function(e){return e.hasErrors()?{values:{},errors:r(o(e.getErrors(),"all"===u.criteriaMode),u.fields)}:{values:s,errors:{}}};return Promise.resolve("sync"===n.mode?a(t(s)):Promise.resolve(e(t)(s)).then(a))}catch(r){return Promise.reject(r)}}};export{t as vestResolver};
|
|
2
|
+
//# sourceMappingURL=vest.module.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vest.module.js","sources":["../src/vest.ts"],"sourcesContent":["import { toNestError } from '@hookform/resolvers';\nimport { FieldError } from 'react-hook-form';\nimport promisify from 'vest/promisify';\nimport type { VestErrors, Resolver } from './types';\n\nconst parseErrorSchema = (\n vestError: VestErrors,\n validateAllFieldCriteria: boolean,\n) => {\n const errors: Record<string, FieldError> = {};\n for (const path in vestError) {\n if (!errors[path]) {\n errors[path] = { message: vestError[path][0], type: '' };\n }\n\n if (validateAllFieldCriteria) {\n errors[path].types = vestError[path].reduce<Record<number, string>>(\n (acc, message, index) => (acc[index] = message) && acc,\n {},\n );\n }\n }\n return errors;\n};\n\nexport const vestResolver: Resolver = (\n schema,\n _,\n resolverOptions = {},\n) => async (values, _context, options) => {\n const result =\n resolverOptions.mode === 'sync'\n ? schema(values)\n : await promisify(schema)(values);\n\n return result.hasErrors()\n ? {\n values: {},\n errors: toNestError(\n parseErrorSchema(result.getErrors(), options.criteriaMode === 'all'),\n options.fields,\n ),\n }\n : { values, errors: {} };\n};\n"],"names":["parseErrorSchema","vestError","validateAllFieldCriteria","errors","path","message","type","types","reduce","acc","index","vestResolver","schema","_","resolverOptions","values","_context","options","result","hasErrors","toNestError","getErrors","criteriaMode","fields","mode","promisify"],"mappings":"gFAKA,IAAMA,EAAmB,SACvBC,EACAC,GAEA,IAAMC,EAAqC,GAC3C,IAAK,IAAMC,KAAQH,EACZE,EAAOC,KACVD,EAAOC,GAAQ,CAAEC,QAASJ,EAAUG,GAAM,GAAIE,KAAM,KAGlDJ,IACFC,EAAOC,GAAMG,MAAQN,EAAUG,GAAMI,OACnC,SAACC,EAAKJ,EAASK,UAAWD,EAAIC,GAASL,IAAYI,GACnD,KAIN,OAAON,GAGIQ,EAAyB,SACpCC,EACAC,EACAC,mBAAAA,IAAAA,EAAkB,aACRC,EAAQC,EAAUC,sBACtBC,GAKN,OAAOA,EAAOC,YACV,CACEJ,OAAQ,GACRZ,OAAQiB,EACNpB,EAAiBkB,EAAOG,YAAsC,QAAzBJ,EAAQK,cAC7CL,EAAQM,SAGZ,CAAER,OAAAA,EAAQZ,OAAQ,4BAZK,SAAzBW,EAAgBU,OACZZ,EAAOG,oBACDU,EAAUb,EAAVa,CAAkBV,aARM"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
!function(e,r){"object"==typeof exports&&"undefined"!=typeof module?r(exports,require("@hookform/resolvers"),require("vest/promisify")):"function"==typeof define&&define.amd?define(["exports","@hookform/resolvers","vest/promisify"],r):r((e||self).hookformResolversVest={},e.hookformResolvers,e.promisify)}(this,function(e,r,o){function t(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}var s=t(o),n=function(e,r){var o={};for(var t in e)o[t]||(o[t]={message:e[t][0],type:""}),r&&(o[t].types=e[t].reduce(function(e,r,o){return(e[o]=r)&&e},{}));return o};e.vestResolver=function(e,o,t){return void 0===t&&(t={}),function(o,i,f){try{var u=function(e){return e.hasErrors()?{values:{},errors:r.toNestError(n(e.getErrors(),"all"===f.criteriaMode),f.fields)}:{values:o,errors:{}}};return Promise.resolve("sync"===t.mode?u(e(o)):Promise.resolve(s.default(e)(o)).then(u))}catch(e){return Promise.reject(e)}}}});
|
|
2
|
+
//# sourceMappingURL=vest.umd.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vest.umd.js","sources":["../src/vest.ts"],"sourcesContent":["import { toNestError } from '@hookform/resolvers';\nimport { FieldError } from 'react-hook-form';\nimport promisify from 'vest/promisify';\nimport type { VestErrors, Resolver } from './types';\n\nconst parseErrorSchema = (\n vestError: VestErrors,\n validateAllFieldCriteria: boolean,\n) => {\n const errors: Record<string, FieldError> = {};\n for (const path in vestError) {\n if (!errors[path]) {\n errors[path] = { message: vestError[path][0], type: '' };\n }\n\n if (validateAllFieldCriteria) {\n errors[path].types = vestError[path].reduce<Record<number, string>>(\n (acc, message, index) => (acc[index] = message) && acc,\n {},\n );\n }\n }\n return errors;\n};\n\nexport const vestResolver: Resolver = (\n schema,\n _,\n resolverOptions = {},\n) => async (values, _context, options) => {\n const result =\n resolverOptions.mode === 'sync'\n ? schema(values)\n : await promisify(schema)(values);\n\n return result.hasErrors()\n ? {\n values: {},\n errors: toNestError(\n parseErrorSchema(result.getErrors(), options.criteriaMode === 'all'),\n options.fields,\n ),\n }\n : { values, errors: {} };\n};\n"],"names":["parseErrorSchema","vestError","validateAllFieldCriteria","errors","path","message","type","types","reduce","acc","index","schema","_","resolverOptions","values","_context","options","result","hasErrors","toNestError","getErrors","criteriaMode","fields","mode","promisify"],"mappings":"scAKMA,EAAmB,SACvBC,EACAC,GAEA,IAAMC,EAAqC,GAC3C,IAAK,IAAMC,KAAQH,EACZE,EAAOC,KACVD,EAAOC,GAAQ,CAAEC,QAASJ,EAAUG,GAAM,GAAIE,KAAM,KAGlDJ,IACFC,EAAOC,GAAMG,MAAQN,EAAUG,GAAMI,OACnC,SAACC,EAAKJ,EAASK,UAAWD,EAAIC,GAASL,IAAYI,GACnD,KAIN,OAAON,kBAG6B,SACpCQ,EACAC,EACAC,mBAAAA,IAAAA,EAAkB,aACRC,EAAQC,EAAUC,sBACtBC,GAKN,OAAOA,EAAOC,YACV,CACEJ,OAAQ,GACRX,OAAQgB,cACNnB,EAAiBiB,EAAOG,YAAsC,QAAzBJ,EAAQK,cAC7CL,EAAQM,SAGZ,CAAER,OAAAA,EAAQX,OAAQ,4BAZK,SAAzBU,EAAgBU,OACZZ,EAAOG,oBACDU,UAAUb,EAAVa,CAAkBV,aARM"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "vest",
|
|
3
|
+
"amdName": "hookformResolversVest",
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"private": true,
|
|
6
|
+
"description": "React Hook Form validation resolver: vest",
|
|
7
|
+
"main": "dist/vest.js",
|
|
8
|
+
"module": "dist/vest.module.js",
|
|
9
|
+
"umd:main": "dist/vest.umd.js",
|
|
10
|
+
"source": "src/index.ts",
|
|
11
|
+
"types": "dist/index.d.ts",
|
|
12
|
+
"license": "MIT",
|
|
13
|
+
"peerDependencies": {
|
|
14
|
+
"react-hook-form": ">=6.6.0",
|
|
15
|
+
"@hookform/resolvers": ">=2.0.0",
|
|
16
|
+
"vest": ">=3.0.0"
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render, screen, act } from '@testing-library/react';
|
|
3
|
+
import user from '@testing-library/user-event';
|
|
4
|
+
import { useForm } from 'react-hook-form';
|
|
5
|
+
import vest from 'vest';
|
|
6
|
+
import { vestResolver } from '..';
|
|
7
|
+
|
|
8
|
+
interface FormData {
|
|
9
|
+
username: string;
|
|
10
|
+
password: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const validationSuite = vest.create('form', (data: FormData) => {
|
|
14
|
+
vest.test('username', 'Username is required', () => {
|
|
15
|
+
vest.enforce(data.username).isNotEmpty();
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
vest.test('password', 'Password must contain a symbol', () => {
|
|
19
|
+
vest.enforce(data.password).matches(/[^A-Za-z0-9]/);
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
interface Props {
|
|
24
|
+
onSubmit: (data: FormData) => void;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function TestComponent({ onSubmit }: Props) {
|
|
28
|
+
const {
|
|
29
|
+
register,
|
|
30
|
+
formState: { errors },
|
|
31
|
+
handleSubmit,
|
|
32
|
+
} = useForm<FormData>({
|
|
33
|
+
resolver: vestResolver(validationSuite), // Useful to check TypeScript regressions
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
<form onSubmit={handleSubmit(onSubmit)}>
|
|
38
|
+
<input {...register('username')} />
|
|
39
|
+
{errors.username && <span role="alert">{errors.username.message}</span>}
|
|
40
|
+
|
|
41
|
+
<input {...register('password')} />
|
|
42
|
+
{errors.password && <span role="alert">{errors.password.message}</span>}
|
|
43
|
+
|
|
44
|
+
<button type="submit">submit</button>
|
|
45
|
+
</form>
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
test("form's validation with Vest and TypeScript's integration", async () => {
|
|
50
|
+
const handleSubmit = jest.fn();
|
|
51
|
+
render(<TestComponent onSubmit={handleSubmit} />);
|
|
52
|
+
|
|
53
|
+
expect(screen.queryAllByRole(/alert/i)).toHaveLength(0);
|
|
54
|
+
|
|
55
|
+
await act(async () => {
|
|
56
|
+
user.click(screen.getByText(/submit/i));
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
expect(screen.getByText(/Username is required/i)).toBeInTheDocument();
|
|
60
|
+
expect(
|
|
61
|
+
screen.getByText(/Password must contain a symbol/i),
|
|
62
|
+
).toBeInTheDocument();
|
|
63
|
+
expect(handleSubmit).not.toHaveBeenCalled();
|
|
64
|
+
});
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { Field, InternalFieldName } from 'react-hook-form';
|
|
2
|
+
import * as vest from 'vest';
|
|
3
|
+
|
|
4
|
+
export const validationSuite = vest.create('form', (data: any = {}) => {
|
|
5
|
+
vest.test('username', 'Username is required', () => {
|
|
6
|
+
vest.enforce(data.username).isNotEmpty();
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
vest.test('username', 'Must be longer than 3 chars', () => {
|
|
10
|
+
vest.enforce(data.username).longerThan(3);
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
vest.test('deepObject.data', 'deepObject.data is required', () => {
|
|
14
|
+
vest.enforce(data.deepObject.data).isNotEmpty();
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
vest.test('password', 'Password is required', () => {
|
|
18
|
+
vest.enforce(data.password).isNotEmpty();
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
vest.test('password', 'Password must be at least 5 chars', () => {
|
|
22
|
+
vest.enforce(data.password).longerThanOrEquals(5);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
vest.test('password', 'Password must contain a digit', () => {
|
|
26
|
+
vest.enforce(data.password).matches(/[0-9]/);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
vest.test('password', 'Password must contain a symbol', () => {
|
|
30
|
+
vest.enforce(data.password).matches(/[^A-Za-z0-9]/);
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
export const validData = {
|
|
35
|
+
username: 'asdda',
|
|
36
|
+
password: 'asddfg123!',
|
|
37
|
+
deepObject: {
|
|
38
|
+
data: 'test',
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export const invalidData = {
|
|
43
|
+
username: '',
|
|
44
|
+
password: 'a',
|
|
45
|
+
deepObject: {
|
|
46
|
+
data: '',
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
export const fields: Record<InternalFieldName, Field['_f']> = {
|
|
51
|
+
username: {
|
|
52
|
+
ref: { name: 'username' },
|
|
53
|
+
name: 'username',
|
|
54
|
+
},
|
|
55
|
+
password: {
|
|
56
|
+
ref: { name: 'password' },
|
|
57
|
+
name: 'password',
|
|
58
|
+
},
|
|
59
|
+
email: {
|
|
60
|
+
ref: { name: 'email' },
|
|
61
|
+
name: 'email',
|
|
62
|
+
},
|
|
63
|
+
birthday: {
|
|
64
|
+
ref: { name: 'birthday' },
|
|
65
|
+
name: 'birthday',
|
|
66
|
+
},
|
|
67
|
+
};
|