Skip to content

Commit 09edc1f

Browse files
joaom00João Pedro Magalhães
andauthored
test(react-form): add on change validation tests (#425)
Co-authored-by: João Pedro Magalhães <[email protected]>
1 parent c6b246d commit 09edc1f

File tree

3 files changed

+216
-1
lines changed

3 files changed

+216
-1
lines changed
Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
import * as React from 'react'
2+
import { render, waitFor } from '@testing-library/react'
3+
import userEvent from '@testing-library/user-event'
4+
import '@testing-library/jest-dom'
5+
import { createFormFactory } from '..'
6+
import { sleep } from './utils'
7+
8+
const user = userEvent.setup()
9+
10+
describe('useField', () => {
11+
it('should allow to set default value', () => {
12+
type Person = {
13+
firstName: string
14+
lastName: string
15+
}
16+
17+
const formFactory = createFormFactory<Person>()
18+
19+
function Comp() {
20+
const form = formFactory.useForm()
21+
22+
return (
23+
<form.Provider>
24+
<form.Field
25+
name="firstName"
26+
defaultValue="FirstName"
27+
children={(field) => {
28+
return (
29+
<input data-testid="fieldinput" {...field.getInputProps()} />
30+
)
31+
}}
32+
/>
33+
</form.Provider>
34+
)
35+
}
36+
37+
const { getByTestId } = render(<Comp />)
38+
const input = getByTestId('fieldinput')
39+
expect(input).toHaveValue('FirstName')
40+
})
41+
42+
it('should not validate on change if isTouched is false', async () => {
43+
type Person = {
44+
firstName: string
45+
lastName: string
46+
}
47+
const error = 'Please enter a different value'
48+
49+
const formFactory = createFormFactory<Person>()
50+
51+
function Comp() {
52+
const form = formFactory.useForm()
53+
54+
return (
55+
<form.Provider>
56+
<form.Field
57+
name="firstName"
58+
onChange={(value) => (value === 'other' ? error : undefined)}
59+
children={(field) => (
60+
<div>
61+
<input
62+
data-testid="fieldinput"
63+
name={field.name}
64+
{...field.getInputProps()}
65+
/>
66+
<p>{field.getMeta().error}</p>
67+
</div>
68+
)}
69+
/>
70+
</form.Provider>
71+
)
72+
}
73+
74+
const { getByTestId, queryByText } = render(<Comp />)
75+
const input = getByTestId('fieldinput')
76+
await user.type(input, 'other')
77+
expect(queryByText(error)).not.toBeInTheDocument()
78+
})
79+
80+
it('should validate on change if isTouched is true', async () => {
81+
type Person = {
82+
firstName: string
83+
lastName: string
84+
}
85+
const error = 'Please enter a different value'
86+
87+
const formFactory = createFormFactory<Person>()
88+
89+
function Comp() {
90+
const form = formFactory.useForm()
91+
92+
return (
93+
<form.Provider>
94+
<form.Field
95+
name="firstName"
96+
defaultMeta={{ isTouched: true }}
97+
onChange={(value) => (value === 'other' ? error : undefined)}
98+
children={(field) => (
99+
<div>
100+
<input
101+
data-testid="fieldinput"
102+
name={field.name}
103+
{...field.getInputProps()}
104+
/>
105+
<p>{field.getMeta().error}</p>
106+
</div>
107+
)}
108+
/>
109+
</form.Provider>
110+
)
111+
}
112+
113+
const { getByTestId, getByText, queryByText } = render(<Comp />)
114+
const input = getByTestId('fieldinput')
115+
expect(queryByText(error)).not.toBeInTheDocument()
116+
await user.type(input, 'other')
117+
expect(getByText(error)).toBeInTheDocument()
118+
})
119+
120+
it('should validate async on change', async () => {
121+
type Person = {
122+
firstName: string
123+
lastName: string
124+
}
125+
const error = 'Please enter a different value'
126+
127+
const formFactory = createFormFactory<Person>()
128+
129+
function Comp() {
130+
const form = formFactory.useForm()
131+
132+
return (
133+
<form.Provider>
134+
<form.Field
135+
name="firstName"
136+
defaultMeta={{ isTouched: true }}
137+
onChangeAsync={async () => {
138+
await sleep(10)
139+
return error
140+
}}
141+
children={(field) => (
142+
<div>
143+
<input
144+
data-testid="fieldinput"
145+
name={field.name}
146+
{...field.getInputProps()}
147+
/>
148+
<p>{field.getMeta().error}</p>
149+
</div>
150+
)}
151+
/>
152+
</form.Provider>
153+
)
154+
}
155+
156+
const { getByTestId, getByText, queryByText } = render(<Comp />)
157+
const input = getByTestId('fieldinput')
158+
await user.type(input, 'other')
159+
expect(queryByText(error)).not.toBeInTheDocument()
160+
await waitFor(() => getByText(error))
161+
expect(getByText(error)).toBeInTheDocument()
162+
})
163+
164+
it('should validate async on change with debounce', async () => {
165+
type Person = {
166+
firstName: string
167+
lastName: string
168+
}
169+
const mockFn = vi.fn()
170+
const error = 'Please enter a different value'
171+
const formFactory = createFormFactory<Person>()
172+
173+
function Comp() {
174+
const form = formFactory.useForm()
175+
176+
return (
177+
<form.Provider>
178+
<form.Field
179+
name="firstName"
180+
defaultMeta={{ isTouched: true }}
181+
onChangeAsyncDebounceMs={10}
182+
onChangeAsync={async () => {
183+
mockFn()
184+
await sleep(10)
185+
return error
186+
}}
187+
children={(field) => (
188+
<div>
189+
<input
190+
data-testid="fieldinput"
191+
name={field.name}
192+
{...field.getInputProps()}
193+
/>
194+
<p>{field.getMeta().error}</p>
195+
</div>
196+
)}
197+
/>
198+
</form.Provider>
199+
)
200+
}
201+
202+
const { getByTestId, getByText } = render(<Comp />)
203+
const input = getByTestId('fieldinput')
204+
await user.type(input, 'other')
205+
// mockFn will have been called 5 times without onChangeAsyncDebounceMs
206+
expect(mockFn).toHaveBeenCalledTimes(0)
207+
await waitFor(() => getByText(error))
208+
expect(getByText(error)).toBeInTheDocument()
209+
})
210+
})

packages/react-form/src/tests/useForm.test.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { fireEvent, render } from '@testing-library/react'
1+
import { render } from '@testing-library/react'
22
import userEvent from '@testing-library/user-event'
33
import '@testing-library/jest-dom'
44
import * as React from 'react'
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export function sleep(timeout: number): Promise<void> {
2+
return new Promise((resolve, _reject) => {
3+
setTimeout(resolve, timeout)
4+
})
5+
}

0 commit comments

Comments
 (0)