Skip to content

Commit 41bda55

Browse files
committed
refactor(form-core): adjust unit tests for parseValuesWithSchema
1 parent bd8456c commit 41bda55

File tree

8 files changed

+141
-83
lines changed

8 files changed

+141
-83
lines changed

docs/framework/react/guides/validation.md

-1
Original file line numberDiff line numberDiff line change
@@ -510,7 +510,6 @@ If you need even more control over your Standard Schema validation, you can comb
510510
name="age"
511511
asyncDebounceMs={500}
512512
validators={{
513-
onChange: 1500,
514513
onChangeAsync: async ({ value, fieldApi }) => {
515514
const errors = fieldApi.parseValueWithSchema(
516515
z.number().gte(13, 'You must be 13 to make an account'),

packages/form-core/src/FieldApi.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1648,7 +1648,7 @@ export class FieldApi<
16481648
* issues (if any). This method does NOT set any internal errors.
16491649
* @param schema The standard schema to parse this field's value with.
16501650
*/
1651-
parseValueWithSchema(schema: StandardSchemaV1) {
1651+
parseValueWithSchema = (schema: StandardSchemaV1<TData, unknown>) => {
16521652
return standardSchemaValidators.validate(
16531653
{ value: this.state.value, validationSource: 'field' },
16541654
schema,
@@ -1660,7 +1660,7 @@ export class FieldApi<
16601660
* issues (if any). This method does NOT set any internal errors.
16611661
* @param schema The standard schema to parse this field's value with.
16621662
*/
1663-
parseValueWithSchemaAsync(schema: StandardSchemaV1) {
1663+
parseValueWithSchemaAsync = (schema: StandardSchemaV1<TData, unknown>) => {
16641664
return standardSchemaValidators.validateAsync(
16651665
{ value: this.state.value, validationSource: 'field' },
16661666
schema,

packages/form-core/src/FormApi.ts

+6-2
Original file line numberDiff line numberDiff line change
@@ -2072,7 +2072,9 @@ export class FormApi<
20722072
* issues (if any). This method does NOT set any internal errors.
20732073
* @param schema The standard schema to parse the form values with.
20742074
*/
2075-
parseFieldValuesWithSchema(schema: StandardSchemaV1) {
2075+
parseFieldValuesWithSchema = (
2076+
schema: StandardSchemaV1<TFormData, unknown>,
2077+
) => {
20762078
return standardSchemaValidators.validate(
20772079
{ value: this.state.values, validationSource: 'form' },
20782080
schema,
@@ -2084,7 +2086,9 @@ export class FormApi<
20842086
* issues (if any). This method does NOT set any internal errors.
20852087
* @param schema The standard schema to parse the form values with.
20862088
*/
2087-
parseFieldValuesWithSchemaAsync(schema: StandardSchemaV1) {
2089+
parseFieldValuesWithSchemaAsync = (
2090+
schema: StandardSchemaV1<TFormData, unknown>,
2091+
) => {
20882092
return standardSchemaValidators.validateAsync(
20892093
{ value: this.state.values, validationSource: 'form' },
20902094
schema,

packages/form-core/tests/FieldApi.spec.ts

+19-15
Original file line numberDiff line numberDiff line change
@@ -1956,7 +1956,9 @@ describe('field api', () => {
19561956
expect(onBlurMock).toHaveBeenCalledTimes(1)
19571957
})
19581958

1959-
it('should pass the current value to the standard schema when calling parseValueWithSchema', async () => {
1959+
it('should pass the current value to the Standard Schema when calling parseValueWithSchema', async () => {
1960+
const schema = z.string().min(3)
1961+
19601962
const form = new FormApi({
19611963
defaultValues: {
19621964
firstName: '',
@@ -1971,18 +1973,20 @@ describe('field api', () => {
19711973
field.mount()
19721974

19731975
// The schema should complain that the value is too short
1974-
const firstNameSchemaResult = field.parseValueWithSchema(z.string().min(3))
1975-
expect(firstNameSchemaResult).not.toBeUndefined()
1976-
expect(Array.isArray(firstNameSchemaResult)).toBe(true)
1977-
expect(firstNameSchemaResult).not.toHaveLength(0)
1976+
const issueResult = field.parseValueWithSchema(schema)
1977+
expect(issueResult).toBeDefined()
1978+
expect(Array.isArray(issueResult)).toBe(true)
1979+
expect(issueResult?.length).toBeGreaterThan(0)
19781980

19791981
field.setValue('some long name that satisfies firstNameSchemaResult')
19801982
// the schema should now be satisfied
1981-
const successResult = field.parseValueWithSchema(z.string().min(3))
1983+
const successResult = field.parseValueWithSchema(schema)
19821984
expect(successResult).toBeUndefined()
19831985
})
19841986

1985-
it('should pass the current value to the standard schema when calling parseValueWithSchemaAsync', async () => {
1987+
it('should pass the current value to the Standard Schema when calling parseValueWithSchemaAsync', async () => {
1988+
const schema = z.string().min(3)
1989+
19861990
const form = new FormApi({
19871991
defaultValues: {
19881992
firstName: '',
@@ -1997,25 +2001,25 @@ describe('field api', () => {
19972001
field.mount()
19982002

19992003
// The schema should complain that the value is too short
2000-
const firstNamePromise = field.parseValueWithSchemaAsync(z.string().min(3))
2001-
expect(firstNamePromise).toBeInstanceOf(Promise)
2004+
const issuePromise = field.parseValueWithSchemaAsync(schema)
2005+
expect(issuePromise).toBeInstanceOf(Promise)
20022006

2003-
const firstNameSchemaResult = await firstNamePromise
2007+
const issueResult = await issuePromise
20042008

2005-
expect(firstNameSchemaResult).not.toBeUndefined()
2006-
expect(Array.isArray(firstNameSchemaResult)).toBe(true)
2007-
expect(firstNameSchemaResult).not.toHaveLength(0)
2009+
expect(issueResult).toBeDefined()
2010+
expect(Array.isArray(issueResult)).toBe(true)
2011+
expect(issueResult?.length).toBeGreaterThan(0)
20082012

20092013
field.setValue('some long name that satisfies firstNameSchemaResult')
20102014
// the schema should now be satisfied
2011-
const successPromise = field.parseValueWithSchemaAsync(z.string().min(3))
2015+
const successPromise = field.parseValueWithSchemaAsync(schema)
20122016
expect(successPromise).toBeInstanceOf(Promise)
20132017

20142018
const successResult = await successPromise
20152019
expect(successResult).toBeUndefined()
20162020
})
20172021

2018-
it('should throw an error when passing an async schema to parseValueWithSchema', async () => {
2022+
it('should throw an error when passing an async Standard Schema to parseValueWithSchema', async () => {
20192023
const testSchema = z.string().superRefine(async () => {
20202024
await sleep(1000)
20212025
return true

packages/form-core/tests/FieldApi.test-d.ts

+24
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { assertType, describe, it } from 'vitest'
2+
import { z } from 'zod'
23
import { FieldApi, FormApi } from '../src/index'
4+
import type { StandardSchemaV1Issue } from '../src/index'
35

46
it('should type value properly', () => {
57
const form = new FormApi({
@@ -356,3 +358,25 @@ it('should handle "sub-fields" async return types added to the field\'s error ar
356358

357359
assertType<Array<'Testing' | undefined>>(field.getMeta().errors)
358360
})
361+
362+
it('should only have field-level error types returned from parseValueWithSchema and parseValueWithSchemaAsync', () => {
363+
const form = new FormApi({
364+
defaultValues: { name: '' },
365+
})
366+
form.mount()
367+
368+
const field = new FieldApi({
369+
form,
370+
name: 'name',
371+
})
372+
field.mount()
373+
374+
const schema = z.string()
375+
// assert that it doesn't think it's a form-level error
376+
assertType<StandardSchemaV1Issue[] | undefined>(
377+
field.parseValueWithSchema(schema),
378+
)
379+
assertType<Promise<StandardSchemaV1Issue[] | undefined>>(
380+
field.parseValueWithSchemaAsync(schema),
381+
)
382+
})

packages/form-core/tests/FormApi.spec.ts

+25-61
Original file line numberDiff line numberDiff line change
@@ -2909,11 +2909,8 @@ describe('form api', () => {
29092909
expect(form.state.canSubmit).toBe(true)
29102910
})
29112911

2912-
it('should pass the current values to the standard schema when calling parseValuesWithSchema', async () => {
2913-
const nameSchema = z.object({
2914-
name: z.string(),
2915-
})
2916-
const firstNameSchema = z.object({
2912+
it('should pass the current values to the Standard Schema when calling parseValuesWithSchema', async () => {
2913+
const schema = z.object({
29172914
firstName: z.string().min(3),
29182915
})
29192916

@@ -2924,47 +2921,31 @@ describe('form api', () => {
29242921
})
29252922
form.mount()
29262923

2927-
const nameSchemaResult = form.parseFieldValuesWithSchema(nameSchema)
2928-
// Name schema should complain that 'name' is missing in our form
2929-
expect(nameSchemaResult).not.toBeUndefined()
2930-
expect(Array.isArray(nameSchemaResult)).not.toBe(true)
2931-
2932-
expect(nameSchemaResult).toHaveProperty('fields')
2933-
expect(nameSchemaResult).toHaveProperty('form')
2934-
2935-
expect(nameSchemaResult?.fields).toHaveProperty('name')
2936-
expect(Array.isArray(nameSchemaResult?.fields['name'])).toBe(true)
2937-
expect(nameSchemaResult?.fields['name']).not.toHaveLength(0)
2938-
29392924
// First name schema should complain that 'firstName' is too short
2940-
const firstNameSchemaResult =
2941-
form.parseFieldValuesWithSchema(firstNameSchema)
2942-
expect(firstNameSchemaResult).not.toBeUndefined()
2943-
expect(Array.isArray(firstNameSchemaResult)).not.toBe(true)
2925+
const issueResult = form.parseFieldValuesWithSchema(schema)
2926+
expect(issueResult).toBeDefined()
2927+
expect(Array.isArray(issueResult)).toBe(false)
29442928

2945-
expect(firstNameSchemaResult).toHaveProperty('fields')
2946-
expect(firstNameSchemaResult).toHaveProperty('form')
2929+
expect(issueResult).toHaveProperty('fields')
2930+
expect(issueResult).toHaveProperty('form')
29472931

2948-
expect(firstNameSchemaResult?.fields).toHaveProperty('firstName')
2949-
expect(Array.isArray(firstNameSchemaResult?.fields['firstName'])).toBe(true)
2950-
expect(firstNameSchemaResult?.fields['firstName']).not.toHaveLength(0)
2932+
expect(issueResult?.fields).toHaveProperty('firstName')
2933+
expect(Array.isArray(issueResult?.fields['firstName'])).toBe(true)
2934+
expect(issueResult?.fields['firstName']?.length).toBeGreaterThan(0)
29512935

29522936
form.setFieldValue(
29532937
'firstName',
29542938
'some long name that satisfies firstNameSchemaResult',
29552939
)
29562940
// firstName should now be satisfied
2957-
const successResult = form.parseFieldValuesWithSchema(firstNameSchema)
2941+
const successResult = form.parseFieldValuesWithSchema(schema)
29582942
expect(successResult).toBeUndefined()
29592943
})
29602944

2961-
it('should pass the current values to the standard schema when calling parseValuesWithSchemaAsync', async () => {
2945+
it('should pass the current values to the Standard Schema when calling parseValuesWithSchemaAsync', async () => {
29622946
vi.useFakeTimers()
29632947

2964-
const nameSchema = z.object({
2965-
name: z.string(),
2966-
})
2967-
const firstNameSchema = z.object({
2948+
const schema = z.object({
29682949
firstName: z.string().min(3),
29692950
})
29702951

@@ -2975,53 +2956,36 @@ describe('form api', () => {
29752956
})
29762957
form.mount()
29772958

2978-
const nameSchemaPromise = form.parseFieldValuesWithSchemaAsync(nameSchema)
2979-
expect(nameSchemaPromise).toBeInstanceOf(Promise)
2980-
2981-
const nameSchemaResult = await nameSchemaPromise
2982-
2983-
// Name schema should complain that 'name' is missing in our form
2984-
expect(nameSchemaResult).not.toBeUndefined()
2985-
expect(Array.isArray(nameSchemaResult)).not.toBe(true)
2986-
2987-
expect(nameSchemaResult).toHaveProperty('fields')
2988-
expect(nameSchemaResult).toHaveProperty('form')
2989-
2990-
expect(nameSchemaResult?.fields).toHaveProperty('name')
2991-
expect(Array.isArray(nameSchemaResult?.fields['name'])).toBe(true)
2992-
expect(nameSchemaResult?.fields['name']).not.toHaveLength(0)
2993-
29942959
// First name schema should complain that 'firstName' is too short
2995-
const firstNamePromise =
2996-
form.parseFieldValuesWithSchemaAsync(firstNameSchema)
2997-
expect(firstNamePromise).toBeInstanceOf(Promise)
2960+
const issuePromise = form.parseFieldValuesWithSchemaAsync(schema)
2961+
expect(issuePromise).toBeInstanceOf(Promise)
29982962

2999-
const firstNameSchemaResult = await firstNamePromise
2963+
const issueResult = await issuePromise
30002964

3001-
expect(firstNameSchemaResult).not.toBeUndefined()
3002-
expect(Array.isArray(firstNameSchemaResult)).not.toBe(true)
2965+
expect(issueResult).toBeDefined()
2966+
expect(Array.isArray(issueResult)).toBe(false)
30032967

3004-
expect(firstNameSchemaResult).toHaveProperty('fields')
3005-
expect(firstNameSchemaResult).toHaveProperty('form')
2968+
expect(issueResult).toHaveProperty('fields')
2969+
expect(issueResult).toHaveProperty('form')
30062970

3007-
expect(firstNameSchemaResult?.fields).toHaveProperty('firstName')
3008-
expect(Array.isArray(firstNameSchemaResult?.fields['firstName'])).toBe(true)
3009-
expect(firstNameSchemaResult?.fields['firstName']).not.toHaveLength(0)
2971+
expect(issueResult?.fields).toHaveProperty('firstName')
2972+
expect(Array.isArray(issueResult?.fields['firstName'])).toBe(true)
2973+
expect(issueResult?.fields['firstName']?.length).toBeGreaterThan(0)
30102974

30112975
form.setFieldValue(
30122976
'firstName',
30132977
'some long name that satisfies firstNameSchemaResult',
30142978
)
30152979

30162980
// firstName should now be satisfied
3017-
const successPromise = form.parseFieldValuesWithSchemaAsync(firstNameSchema)
2981+
const successPromise = form.parseFieldValuesWithSchemaAsync(schema)
30182982
expect(successPromise).toBeInstanceOf(Promise)
30192983

30202984
const successResult = await successPromise
30212985
expect(successResult).toBeUndefined()
30222986
})
30232987

3024-
it('should throw an error when passing an async schema to parseValuesWithSchema', async () => {
2988+
it('should throw an error when passing an async Standard Schema to parseValuesWithSchema', async () => {
30252989
const testSchema = z
30262990
.object({
30272991
name: z.string(),

packages/form-core/tests/FormApi.test-d.ts

+29-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
import { assertType, it } from 'vitest'
2+
import { z } from 'zod'
23
import { FormApi } from '../src'
3-
import type { ValidationError, ValidationErrorMap } from '../src'
4+
import type {
5+
StandardSchemaV1Issue,
6+
ValidationError,
7+
ValidationErrorMap,
8+
} from '../src'
49

510
it('should return all errors matching the right type from getAllErrors', () => {
611
const form = new FormApi({
@@ -84,3 +89,26 @@ it('should type handleChange correctly', () => {
8489
form.handleSubmit,
8590
)
8691
})
92+
93+
type FormLevelStandardSchemaIssue = {
94+
form: Record<string, StandardSchemaV1Issue[]>
95+
fields: Record<string, StandardSchemaV1Issue[]>
96+
}
97+
98+
it('should only have form-level error types returned from parseFieldValuesWithSchema and parseFieldValuesWithSchemaAsync', () => {
99+
const form = new FormApi({
100+
defaultValues: { name: '' },
101+
})
102+
form.mount()
103+
104+
const schema = z.object({
105+
name: z.string(),
106+
})
107+
// assert that it doesn't think it's a field-level error
108+
assertType<FormLevelStandardSchemaIssue | undefined>(
109+
form.parseFieldValuesWithSchema(schema),
110+
)
111+
assertType<Promise<FormLevelStandardSchemaIssue | undefined>>(
112+
form.parseFieldValuesWithSchemaAsync(schema),
113+
)
114+
})

packages/form-core/tests/standardSchemaValidator.test-d.ts

+36-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { assertType, describe, it } from 'vitest'
22
import { z } from 'zod'
3-
import { FieldApi, FormApi } from '../src/index'
3+
import { FieldApi, FormApi, standardSchemaValidators } from '../src/index'
44
import type { StandardSchemaV1Issue } from '../src/index'
55

66
describe('standard schema validator', () => {
@@ -56,4 +56,39 @@ describe('standard schema validator', () => {
5656
field.getMeta().errorMap.onChange,
5757
)
5858
})
59+
60+
type FormLevelStandardSchemaIssue = {
61+
form: Record<string, StandardSchemaV1Issue[]>
62+
fields: Record<string, StandardSchemaV1Issue[]>
63+
}
64+
65+
it('Should return different Standard Schema Issue types from validate based on scope', () => {
66+
const formSourceError = standardSchemaValidators.validate(
67+
{ value: '', validationSource: 'form' },
68+
z.string(),
69+
)
70+
const fieldSourceError = standardSchemaValidators.validate(
71+
{ value: '', validationSource: 'field' },
72+
z.string(),
73+
)
74+
75+
assertType<FormLevelStandardSchemaIssue | undefined>(formSourceError)
76+
assertType<StandardSchemaV1Issue[] | undefined>(fieldSourceError)
77+
})
78+
79+
it('Should return different Standard Schema Issue types from validateAsync based on scope', () => {
80+
const formSourceError = standardSchemaValidators.validateAsync(
81+
{ value: '', validationSource: 'form' },
82+
z.string(),
83+
)
84+
const fieldSourceError = standardSchemaValidators.validateAsync(
85+
{ value: '', validationSource: 'field' },
86+
z.string(),
87+
)
88+
89+
assertType<Promise<FormLevelStandardSchemaIssue | undefined>>(
90+
formSourceError,
91+
)
92+
assertType<Promise<StandardSchemaV1Issue[] | undefined>>(fieldSourceError)
93+
})
5994
})

0 commit comments

Comments
 (0)