@hookform/resolvers 5.4.1 → 5.4.3

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.
Files changed (64) hide show
  1. package/arktype/src/__tests__/__snapshots__/arktype.ts.snap +11 -11
  2. package/arktype/src/__tests__/arktype.ts +15 -6
  3. package/computed-types/src/__tests__/computed-types.ts +15 -6
  4. package/effect-ts/src/__tests__/effect-ts.ts +15 -6
  5. package/io-ts/src/__tests__/io-ts.ts +15 -6
  6. package/package.json +1 -1
  7. package/standard-schema/dist/standard-schema.js +1 -1
  8. package/standard-schema/dist/standard-schema.js.map +1 -1
  9. package/standard-schema/dist/standard-schema.mjs +1 -1
  10. package/standard-schema/dist/standard-schema.modern.mjs +1 -1
  11. package/standard-schema/dist/standard-schema.modern.mjs.map +1 -1
  12. package/standard-schema/dist/standard-schema.module.js +1 -1
  13. package/standard-schema/dist/standard-schema.module.js.map +1 -1
  14. package/standard-schema/dist/standard-schema.umd.js +1 -1
  15. package/standard-schema/dist/standard-schema.umd.js.map +1 -1
  16. package/standard-schema/src/__tests__/__snapshots__/standard-schema.ts.snap +31 -0
  17. package/standard-schema/src/__tests__/standard-schema.ts +126 -13
  18. package/standard-schema/src/standard-schema.ts +62 -14
  19. package/superstruct/dist/superstruct.d.ts +5 -0
  20. package/superstruct/dist/superstruct.js.map +1 -1
  21. package/superstruct/dist/superstruct.modern.mjs.map +1 -1
  22. package/superstruct/dist/superstruct.module.js.map +1 -1
  23. package/superstruct/dist/superstruct.umd.js.map +1 -1
  24. package/superstruct/src/__tests__/superstruct.ts +22 -8
  25. package/superstruct/src/superstruct.ts +13 -0
  26. package/typanion/src/__tests__/typanion.ts +8 -3
  27. package/typebox/src/__tests__/typebox.ts +31 -18
  28. package/typeschema/dist/typeschema.js +1 -1
  29. package/typeschema/dist/typeschema.js.map +1 -1
  30. package/typeschema/dist/typeschema.mjs +1 -1
  31. package/typeschema/dist/typeschema.modern.mjs +1 -1
  32. package/typeschema/dist/typeschema.modern.mjs.map +1 -1
  33. package/typeschema/dist/typeschema.module.js +1 -1
  34. package/typeschema/dist/typeschema.module.js.map +1 -1
  35. package/typeschema/dist/typeschema.umd.js +1 -1
  36. package/typeschema/dist/typeschema.umd.js.map +1 -1
  37. package/typeschema/src/__tests__/typeschema.ts +23 -15
  38. package/typeschema/src/typeschema.ts +6 -6
  39. package/valibot/src/__tests__/valibot.ts +15 -6
  40. package/vine/src/__tests__/vine.ts +15 -6
  41. package/yup/dist/yup.d.ts +2 -2
  42. package/yup/dist/yup.js.map +1 -1
  43. package/yup/dist/yup.modern.mjs.map +1 -1
  44. package/yup/dist/yup.module.js.map +1 -1
  45. package/yup/dist/yup.umd.js.map +1 -1
  46. package/yup/src/__tests__/yup.ts +15 -6
  47. package/yup/src/yup.ts +10 -2
  48. package/zod/dist/zod.d.ts +8 -2
  49. package/zod/dist/zod.js +1 -1
  50. package/zod/dist/zod.js.map +1 -1
  51. package/zod/dist/zod.mjs +1 -1
  52. package/zod/dist/zod.modern.mjs +1 -1
  53. package/zod/dist/zod.modern.mjs.map +1 -1
  54. package/zod/dist/zod.module.js +1 -1
  55. package/zod/dist/zod.module.js.map +1 -1
  56. package/zod/dist/zod.umd.js +1 -1
  57. package/zod/dist/zod.umd.js.map +1 -1
  58. package/zod/src/__tests__/__snapshots__/zod-v3.ts.snap +430 -0
  59. package/zod/src/__tests__/__snapshots__/zod-v4-mini.ts.snap +472 -0
  60. package/zod/src/__tests__/__snapshots__/zod-v4.ts.snap +434 -0
  61. package/zod/src/__tests__/zod-v3.ts +45 -6
  62. package/zod/src/__tests__/zod-v4-mini.ts +21 -11
  63. package/zod/src/__tests__/zod-v4.ts +47 -11
  64. package/zod/src/zod.ts +28 -5
@@ -0,0 +1,434 @@
1
+ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
+
3
+ exports[`zodResolver > should return a single error from zodResolver when validation fails 1`] = `
4
+ {
5
+ "errors": {
6
+ "accessToken": {
7
+ "message": "Invalid input: expected string, received undefined",
8
+ "ref": undefined,
9
+ "type": "invalid_type",
10
+ },
11
+ "birthYear": {
12
+ "message": "Invalid input: expected number, received string",
13
+ "ref": undefined,
14
+ "type": "invalid_type",
15
+ },
16
+ "dateStr": {
17
+ "message": "Invalid input: expected string, received undefined",
18
+ "ref": undefined,
19
+ "type": "invalid_type",
20
+ },
21
+ "email": {
22
+ "message": "Invalid email address",
23
+ "ref": {
24
+ "name": "email",
25
+ },
26
+ "type": "invalid_format",
27
+ },
28
+ "enabled": {
29
+ "message": "Invalid input: expected boolean, received undefined",
30
+ "ref": undefined,
31
+ "type": "invalid_type",
32
+ },
33
+ "like": [
34
+ {
35
+ "id": {
36
+ "message": "Invalid input: expected number, received string",
37
+ "ref": undefined,
38
+ "type": "invalid_type",
39
+ },
40
+ "name": {
41
+ "message": "Invalid input: expected string, received undefined",
42
+ "ref": undefined,
43
+ "type": "invalid_type",
44
+ },
45
+ },
46
+ ],
47
+ "password": {
48
+ "message": "One uppercase character",
49
+ "ref": {
50
+ "name": "password",
51
+ },
52
+ "type": "invalid_format",
53
+ },
54
+ "repeatPassword": {
55
+ "message": "Invalid input: expected string, received undefined",
56
+ "ref": undefined,
57
+ "type": "invalid_type",
58
+ },
59
+ "tags": {
60
+ "message": "Invalid input: expected array, received undefined",
61
+ "ref": undefined,
62
+ "type": "invalid_type",
63
+ },
64
+ "url": {
65
+ "message": "Custom error url",
66
+ "ref": undefined,
67
+ "type": "invalid_format",
68
+ },
69
+ "username": {
70
+ "message": "Invalid input: expected string, received undefined",
71
+ "ref": {
72
+ "name": "username",
73
+ },
74
+ "type": "invalid_type",
75
+ },
76
+ },
77
+ "values": {},
78
+ }
79
+ `;
80
+
81
+ exports[`zodResolver > should return a single error from zodResolver with \`mode: sync\` when validation fails 1`] = `
82
+ {
83
+ "errors": {
84
+ "accessToken": {
85
+ "message": "Invalid input: expected string, received undefined",
86
+ "ref": undefined,
87
+ "type": "invalid_type",
88
+ },
89
+ "birthYear": {
90
+ "message": "Invalid input: expected number, received string",
91
+ "ref": undefined,
92
+ "type": "invalid_type",
93
+ },
94
+ "dateStr": {
95
+ "message": "Invalid input: expected string, received undefined",
96
+ "ref": undefined,
97
+ "type": "invalid_type",
98
+ },
99
+ "email": {
100
+ "message": "Invalid email address",
101
+ "ref": {
102
+ "name": "email",
103
+ },
104
+ "type": "invalid_format",
105
+ },
106
+ "enabled": {
107
+ "message": "Invalid input: expected boolean, received undefined",
108
+ "ref": undefined,
109
+ "type": "invalid_type",
110
+ },
111
+ "like": [
112
+ {
113
+ "id": {
114
+ "message": "Invalid input: expected number, received string",
115
+ "ref": undefined,
116
+ "type": "invalid_type",
117
+ },
118
+ "name": {
119
+ "message": "Invalid input: expected string, received undefined",
120
+ "ref": undefined,
121
+ "type": "invalid_type",
122
+ },
123
+ },
124
+ ],
125
+ "password": {
126
+ "message": "One uppercase character",
127
+ "ref": {
128
+ "name": "password",
129
+ },
130
+ "type": "invalid_format",
131
+ },
132
+ "repeatPassword": {
133
+ "message": "Invalid input: expected string, received undefined",
134
+ "ref": undefined,
135
+ "type": "invalid_type",
136
+ },
137
+ "tags": {
138
+ "message": "Invalid input: expected array, received undefined",
139
+ "ref": undefined,
140
+ "type": "invalid_type",
141
+ },
142
+ "url": {
143
+ "message": "Custom error url",
144
+ "ref": undefined,
145
+ "type": "invalid_format",
146
+ },
147
+ "username": {
148
+ "message": "Invalid input: expected string, received undefined",
149
+ "ref": {
150
+ "name": "username",
151
+ },
152
+ "type": "invalid_type",
153
+ },
154
+ },
155
+ "values": {},
156
+ }
157
+ `;
158
+
159
+ exports[`zodResolver > should return all the errors from zodResolver when validation fails with \`validateAllFieldCriteria\` set to true 1`] = `
160
+ {
161
+ "errors": {
162
+ "accessToken": {
163
+ "message": "Invalid input: expected string, received undefined",
164
+ "ref": undefined,
165
+ "type": "invalid_type",
166
+ "types": {
167
+ "invalid_type": [
168
+ "Invalid input: expected string, received undefined",
169
+ "Invalid input: expected number, received undefined",
170
+ ],
171
+ "invalid_union": "Invalid input",
172
+ },
173
+ },
174
+ "birthYear": {
175
+ "message": "Invalid input: expected number, received string",
176
+ "ref": undefined,
177
+ "type": "invalid_type",
178
+ "types": {
179
+ "invalid_type": "Invalid input: expected number, received string",
180
+ },
181
+ },
182
+ "dateStr": {
183
+ "message": "Invalid input: expected string, received undefined",
184
+ "ref": undefined,
185
+ "type": "invalid_type",
186
+ "types": {
187
+ "invalid_type": "Invalid input: expected string, received undefined",
188
+ },
189
+ },
190
+ "email": {
191
+ "message": "Invalid email address",
192
+ "ref": {
193
+ "name": "email",
194
+ },
195
+ "type": "invalid_format",
196
+ "types": {
197
+ "invalid_format": "Invalid email address",
198
+ },
199
+ },
200
+ "enabled": {
201
+ "message": "Invalid input: expected boolean, received undefined",
202
+ "ref": undefined,
203
+ "type": "invalid_type",
204
+ "types": {
205
+ "invalid_type": "Invalid input: expected boolean, received undefined",
206
+ },
207
+ },
208
+ "like": [
209
+ {
210
+ "id": {
211
+ "message": "Invalid input: expected number, received string",
212
+ "ref": undefined,
213
+ "type": "invalid_type",
214
+ "types": {
215
+ "invalid_type": "Invalid input: expected number, received string",
216
+ },
217
+ },
218
+ "name": {
219
+ "message": "Invalid input: expected string, received undefined",
220
+ "ref": undefined,
221
+ "type": "invalid_type",
222
+ "types": {
223
+ "invalid_type": "Invalid input: expected string, received undefined",
224
+ },
225
+ },
226
+ },
227
+ ],
228
+ "password": {
229
+ "message": "One uppercase character",
230
+ "ref": {
231
+ "name": "password",
232
+ },
233
+ "type": "invalid_format",
234
+ "types": {
235
+ "invalid_format": [
236
+ "One uppercase character",
237
+ "One lowercase character",
238
+ "One number",
239
+ ],
240
+ "too_small": "Must be at least 8 characters in length",
241
+ },
242
+ },
243
+ "repeatPassword": {
244
+ "message": "Invalid input: expected string, received undefined",
245
+ "ref": undefined,
246
+ "type": "invalid_type",
247
+ "types": {
248
+ "invalid_type": "Invalid input: expected string, received undefined",
249
+ },
250
+ },
251
+ "tags": {
252
+ "message": "Invalid input: expected array, received undefined",
253
+ "ref": undefined,
254
+ "type": "invalid_type",
255
+ "types": {
256
+ "invalid_type": "Invalid input: expected array, received undefined",
257
+ },
258
+ },
259
+ "url": {
260
+ "message": "Custom error url",
261
+ "ref": undefined,
262
+ "type": "invalid_format",
263
+ "types": {
264
+ "invalid_format": "Custom error url",
265
+ "invalid_union": "Invalid input",
266
+ "invalid_value": "Invalid input: expected """,
267
+ },
268
+ },
269
+ "username": {
270
+ "message": "Invalid input: expected string, received undefined",
271
+ "ref": {
272
+ "name": "username",
273
+ },
274
+ "type": "invalid_type",
275
+ "types": {
276
+ "invalid_type": "Invalid input: expected string, received undefined",
277
+ },
278
+ },
279
+ },
280
+ "values": {},
281
+ }
282
+ `;
283
+
284
+ exports[`zodResolver > should return all the errors from zodResolver when validation fails with \`validateAllFieldCriteria\` set to true and \`mode: sync\` 1`] = `
285
+ {
286
+ "errors": {
287
+ "accessToken": {
288
+ "message": "Invalid input: expected string, received undefined",
289
+ "ref": undefined,
290
+ "type": "invalid_type",
291
+ "types": {
292
+ "invalid_type": [
293
+ "Invalid input: expected string, received undefined",
294
+ "Invalid input: expected number, received undefined",
295
+ ],
296
+ "invalid_union": "Invalid input",
297
+ },
298
+ },
299
+ "birthYear": {
300
+ "message": "Invalid input: expected number, received string",
301
+ "ref": undefined,
302
+ "type": "invalid_type",
303
+ "types": {
304
+ "invalid_type": "Invalid input: expected number, received string",
305
+ },
306
+ },
307
+ "dateStr": {
308
+ "message": "Invalid input: expected string, received undefined",
309
+ "ref": undefined,
310
+ "type": "invalid_type",
311
+ "types": {
312
+ "invalid_type": "Invalid input: expected string, received undefined",
313
+ },
314
+ },
315
+ "email": {
316
+ "message": "Invalid email address",
317
+ "ref": {
318
+ "name": "email",
319
+ },
320
+ "type": "invalid_format",
321
+ "types": {
322
+ "invalid_format": "Invalid email address",
323
+ },
324
+ },
325
+ "enabled": {
326
+ "message": "Invalid input: expected boolean, received undefined",
327
+ "ref": undefined,
328
+ "type": "invalid_type",
329
+ "types": {
330
+ "invalid_type": "Invalid input: expected boolean, received undefined",
331
+ },
332
+ },
333
+ "like": [
334
+ {
335
+ "id": {
336
+ "message": "Invalid input: expected number, received string",
337
+ "ref": undefined,
338
+ "type": "invalid_type",
339
+ "types": {
340
+ "invalid_type": "Invalid input: expected number, received string",
341
+ },
342
+ },
343
+ "name": {
344
+ "message": "Invalid input: expected string, received undefined",
345
+ "ref": undefined,
346
+ "type": "invalid_type",
347
+ "types": {
348
+ "invalid_type": "Invalid input: expected string, received undefined",
349
+ },
350
+ },
351
+ },
352
+ ],
353
+ "password": {
354
+ "message": "One uppercase character",
355
+ "ref": {
356
+ "name": "password",
357
+ },
358
+ "type": "invalid_format",
359
+ "types": {
360
+ "invalid_format": [
361
+ "One uppercase character",
362
+ "One lowercase character",
363
+ "One number",
364
+ ],
365
+ "too_small": "Must be at least 8 characters in length",
366
+ },
367
+ },
368
+ "repeatPassword": {
369
+ "message": "Invalid input: expected string, received undefined",
370
+ "ref": undefined,
371
+ "type": "invalid_type",
372
+ "types": {
373
+ "invalid_type": "Invalid input: expected string, received undefined",
374
+ },
375
+ },
376
+ "tags": {
377
+ "message": "Invalid input: expected array, received undefined",
378
+ "ref": undefined,
379
+ "type": "invalid_type",
380
+ "types": {
381
+ "invalid_type": "Invalid input: expected array, received undefined",
382
+ },
383
+ },
384
+ "url": {
385
+ "message": "Custom error url",
386
+ "ref": undefined,
387
+ "type": "invalid_format",
388
+ "types": {
389
+ "invalid_format": "Custom error url",
390
+ "invalid_union": "Invalid input",
391
+ "invalid_value": "Invalid input: expected """,
392
+ },
393
+ },
394
+ "username": {
395
+ "message": "Invalid input: expected string, received undefined",
396
+ "ref": {
397
+ "name": "username",
398
+ },
399
+ "type": "invalid_type",
400
+ "types": {
401
+ "invalid_type": "Invalid input: expected string, received undefined",
402
+ },
403
+ },
404
+ },
405
+ "values": {},
406
+ }
407
+ `;
408
+
409
+ exports[`zodResolver > should return parsed values from zodResolver with \`mode: sync\` when validation pass 1`] = `
410
+ {
411
+ "errors": {},
412
+ "values": {
413
+ "accessToken": "accessToken",
414
+ "birthYear": 2000,
415
+ "dateStr": 2020-01-01T00:00:00.000Z,
416
+ "email": "john@doe.com",
417
+ "enabled": true,
418
+ "like": [
419
+ {
420
+ "id": 1,
421
+ "name": "name",
422
+ },
423
+ ],
424
+ "password": "Password123_",
425
+ "repeatPassword": "Password123_",
426
+ "tags": [
427
+ "tag1",
428
+ "tag2",
429
+ ],
430
+ "url": "https://react-hook-form.com/",
431
+ "username": "Doe",
432
+ },
433
+ }
434
+ `;
@@ -1,3 +1,4 @@
1
+ import { renderHook } from '@testing-library/react';
1
2
  import { Resolver, SubmitHandler, useForm } from 'react-hook-form';
2
3
  import { z } from 'zod/v3';
3
4
  import { zodResolver } from '..';
@@ -80,6 +81,36 @@ describe('zodResolver', () => {
80
81
  expect(result).toMatchSnapshot();
81
82
  });
82
83
 
84
+ it('should surface the error from the union member closest to matching, not just the first member', async () => {
85
+ const branchWithMoreIssues = z.object({
86
+ id: z.number({ invalid_type_error: 'id wrong (branch B)' }),
87
+ name: z.number({ invalid_type_error: 'name wrong (branch B)' }),
88
+ });
89
+ const branchWithFewerIssues = z.object({
90
+ id: z.string(),
91
+ name: z.number({
92
+ invalid_type_error: 'name wrong (branch A, closer match)',
93
+ }),
94
+ });
95
+ // branchWithMoreIssues is listed first, but only branchWithFewerIssues
96
+ // actually comes close to matching the input (it only fails on `name`).
97
+ const unionSchema = z.object({
98
+ target: z.union([branchWithMoreIssues, branchWithFewerIssues]),
99
+ });
100
+
101
+ const result = await zodResolver(unionSchema)(
102
+ { target: { id: 'abc', name: 'xyz' } } as unknown as z.input<
103
+ typeof unionSchema
104
+ >,
105
+ undefined,
106
+ { fields: {}, shouldUseNativeValidation },
107
+ );
108
+
109
+ expect(result.errors).toMatchObject({
110
+ target: { message: 'name wrong (branch A, closer match)' },
111
+ });
112
+ });
113
+
83
114
  it('should throw any error unrelated to Zod', async () => {
84
115
  const schemaWithCustomError = schema.refine(() => {
85
116
  throw Error('custom error');
@@ -147,9 +178,13 @@ describe('zodResolver', () => {
147
178
  it('should correctly infer the output type from a Zod schema for the handleSubmit function in useForm', () => {
148
179
  const schema = z.object({ id: z.number() });
149
180
 
150
- const form = useForm({
151
- resolver: zodResolver(schema),
152
- });
181
+ const {
182
+ result: { current: form },
183
+ } = renderHook(() =>
184
+ useForm({
185
+ resolver: zodResolver(schema),
186
+ }),
187
+ );
153
188
 
154
189
  expectTypeOf(form.watch('id')).toEqualTypeOf<number>();
155
190
 
@@ -163,9 +198,13 @@ describe('zodResolver', () => {
163
198
  it('should correctly infer the output type from a Zod schema with a transform for the handleSubmit function in useForm', () => {
164
199
  const schema = z.object({ id: z.number().transform((val) => String(val)) });
165
200
 
166
- const form = useForm({
167
- resolver: zodResolver(schema),
168
- });
201
+ const {
202
+ result: { current: form },
203
+ } = renderHook(() =>
204
+ useForm({
205
+ resolver: zodResolver(schema),
206
+ }),
207
+ );
169
208
 
170
209
  expectTypeOf(form.watch('id')).toEqualTypeOf<number>();
171
210
 
@@ -1,5 +1,7 @@
1
+ import { renderHook } from '@testing-library/react';
1
2
  import { Resolver, SubmitHandler, useForm } from 'react-hook-form';
2
3
  import { z } from 'zod/v4-mini';
4
+ import * as v4Core from 'zod/v4/core';
3
5
  import { zodResolver } from '..';
4
6
  import {
5
7
  fields,
@@ -12,7 +14,7 @@ const shouldUseNativeValidation = false;
12
14
 
13
15
  describe('zodResolver', () => {
14
16
  it('should return values from zodResolver when validation pass & raw=true', async () => {
15
- const parseAsyncSpy = vi.spyOn(schema, 'parseAsync');
17
+ const parseAsyncSpy = vi.spyOn(v4Core, 'parseAsync');
16
18
 
17
19
  const result = await zodResolver(schema, undefined, {
18
20
  raw: true,
@@ -28,8 +30,8 @@ describe('zodResolver', () => {
28
30
  });
29
31
 
30
32
  it('should return parsed values from zodResolver with `mode: sync` when validation pass', async () => {
31
- const parseSpy = vi.spyOn(schema, 'parse');
32
- const parseAsyncSpy = vi.spyOn(schema, 'parseAsync');
33
+ const parseSpy = vi.spyOn(v4Core, 'parse');
34
+ const parseAsyncSpy = vi.spyOn(v4Core, 'parseAsync');
33
35
 
34
36
  const result = await zodResolver(schema, undefined, {
35
37
  mode: 'sync',
@@ -50,8 +52,8 @@ describe('zodResolver', () => {
50
52
  });
51
53
 
52
54
  it('should return a single error from zodResolver with `mode: sync` when validation fails', async () => {
53
- const parseSpy = vi.spyOn(schema, 'parse');
54
- const parseAsyncSpy = vi.spyOn(schema, 'parseAsync');
55
+ const parseSpy = vi.spyOn(v4Core, 'parse');
56
+ const parseAsyncSpy = vi.spyOn(v4Core, 'parseAsync');
55
57
 
56
58
  const result = await zodResolver(schema, undefined, {
57
59
  mode: 'sync',
@@ -146,9 +148,13 @@ describe('zodResolver', () => {
146
148
  it('should correctly infer the output type from a Zod schema for the handleSubmit function in useForm', () => {
147
149
  const schema = z.object({ id: z.number() });
148
150
 
149
- const form = useForm({
150
- resolver: zodResolver(schema),
151
- });
151
+ const {
152
+ result: { current: form },
153
+ } = renderHook(() =>
154
+ useForm({
155
+ resolver: zodResolver(schema),
156
+ }),
157
+ );
152
158
 
153
159
  expectTypeOf(form.watch('id')).toEqualTypeOf<number>();
154
160
 
@@ -167,9 +173,13 @@ describe('zodResolver', () => {
167
173
  ),
168
174
  });
169
175
 
170
- const form = useForm({
171
- resolver: zodResolver(schema),
172
- });
176
+ const {
177
+ result: { current: form },
178
+ } = renderHook(() =>
179
+ useForm({
180
+ resolver: zodResolver(schema),
181
+ }),
182
+ );
173
183
 
174
184
  expectTypeOf(form.watch('id')).toEqualTypeOf<number>();
175
185
 
@@ -1,5 +1,7 @@
1
+ import { renderHook } from '@testing-library/react';
1
2
  import { Resolver, SubmitHandler, useForm } from 'react-hook-form';
2
3
  import { z } from 'zod/v4';
4
+ import * as v4Core from 'zod/v4/core';
3
5
  import { zodResolver } from '..';
4
6
  import { fields, invalidData, schema, validData } from './__fixtures__/data-v4';
5
7
 
@@ -7,7 +9,7 @@ const shouldUseNativeValidation = false;
7
9
 
8
10
  describe('zodResolver', () => {
9
11
  it('should return values from zodResolver when validation pass & raw=true', async () => {
10
- const parseAsyncSpy = vi.spyOn(schema, 'parseAsync');
12
+ const parseAsyncSpy = vi.spyOn(v4Core, 'parseAsync');
11
13
 
12
14
  const result = await zodResolver(schema, undefined, {
13
15
  raw: true,
@@ -21,8 +23,8 @@ describe('zodResolver', () => {
21
23
  });
22
24
 
23
25
  it('should return parsed values from zodResolver with `mode: sync` when validation pass', async () => {
24
- const parseSpy = vi.spyOn(schema, 'parse');
25
- const parseAsyncSpy = vi.spyOn(schema, 'parseAsync');
26
+ const parseSpy = vi.spyOn(v4Core, 'parse');
27
+ const parseAsyncSpy = vi.spyOn(v4Core, 'parseAsync');
26
28
 
27
29
  const result = await zodResolver(schema, undefined, {
28
30
  mode: 'sync',
@@ -44,8 +46,8 @@ describe('zodResolver', () => {
44
46
  });
45
47
 
46
48
  it('should return a single error from zodResolver with `mode: sync` when validation fails', async () => {
47
- const parseSpy = vi.spyOn(schema, 'parse');
48
- const parseAsyncSpy = vi.spyOn(schema, 'parseAsync');
49
+ const parseSpy = vi.spyOn(v4Core, 'parse');
50
+ const parseAsyncSpy = vi.spyOn(v4Core, 'parseAsync');
49
51
 
50
52
  const result = await zodResolver(schema, undefined, {
51
53
  mode: 'sync',
@@ -80,6 +82,32 @@ describe('zodResolver', () => {
80
82
  expect(result).toMatchSnapshot();
81
83
  });
82
84
 
85
+ it('should surface the error from the union member closest to matching, not just the first member', async () => {
86
+ const branchWithMoreIssues = z.object({
87
+ id: z.string().refine(() => false, { error: 'id wrong (branch B)' }),
88
+ name: z.string().refine(() => false, { error: 'name wrong (branch B)' }),
89
+ });
90
+ const branchWithFewerIssues = z.object({
91
+ id: z.string(),
92
+ name: z
93
+ .string()
94
+ .refine((v) => v.length >= 5, { error: 'name too short' }),
95
+ });
96
+ // branchWithMoreIssues is listed first, but only branchWithFewerIssues
97
+ // actually comes close to matching the input (it only fails on `name`).
98
+ const unionSchema = z.union([branchWithMoreIssues, branchWithFewerIssues]);
99
+
100
+ const result = await zodResolver(unionSchema)(
101
+ { id: 'abc', name: 'yo' },
102
+ undefined,
103
+ { fields: {}, shouldUseNativeValidation },
104
+ );
105
+
106
+ expect(result.errors).toMatchObject({
107
+ '': { message: 'name too short' },
108
+ });
109
+ });
110
+
83
111
  it('should throw any error unrelated to Zod', async () => {
84
112
  const schemaWithCustomError = schema.refine(() => {
85
113
  throw Error('custom error');
@@ -145,9 +173,13 @@ describe('zodResolver', () => {
145
173
  it('should correctly infer the output type from a Zod schema for the handleSubmit function in useForm', () => {
146
174
  const schema = z.object({ id: z.number() });
147
175
 
148
- const form = useForm({
149
- resolver: zodResolver(schema),
150
- });
176
+ const {
177
+ result: { current: form },
178
+ } = renderHook(() =>
179
+ useForm({
180
+ resolver: zodResolver(schema),
181
+ }),
182
+ );
151
183
 
152
184
  expectTypeOf(form.watch('id')).toEqualTypeOf<number>();
153
185
 
@@ -161,9 +193,13 @@ describe('zodResolver', () => {
161
193
  it('should correctly infer the output type from a Zod schema with a transform for the handleSubmit function in useForm', () => {
162
194
  const schema = z.object({ id: z.number().transform((val) => String(val)) });
163
195
 
164
- const form = useForm({
165
- resolver: zodResolver(schema),
166
- });
196
+ const {
197
+ result: { current: form },
198
+ } = renderHook(() =>
199
+ useForm({
200
+ resolver: zodResolver(schema),
201
+ }),
202
+ );
167
203
 
168
204
  expectTypeOf(form.watch('id')).toEqualTypeOf<number>();
169
205