@@ -2,6 +2,7 @@ import { expect } from 'vitest'
2
2
3
3
import { FormApi } from '../FormApi'
4
4
import { FieldApi } from '../FieldApi'
5
+ import { sleep } from './utils'
5
6
6
7
describe ( 'field api' , ( ) => {
7
8
it ( 'should have an initial value' , ( ) => {
@@ -151,7 +152,30 @@ describe('field api', () => {
151
152
expect ( subfield . getValue ( ) ) . toBe ( 'one' )
152
153
} )
153
154
154
- it ( 'should run validation onChange' , async ( ) => {
155
+ it ( 'should not throw errors when no meta info is stored on a field and a form re-renders' , async ( ) => {
156
+ const form = new FormApi ( {
157
+ defaultValues : {
158
+ name : 'test' ,
159
+ } ,
160
+ } )
161
+
162
+ const field = new FieldApi ( {
163
+ form,
164
+ name : 'name' ,
165
+ } )
166
+
167
+ field . mount ( )
168
+
169
+ expect ( ( ) =>
170
+ form . update ( {
171
+ defaultValues : {
172
+ name : 'other' ,
173
+ } ,
174
+ } ) ,
175
+ ) . not . toThrow ( )
176
+ } )
177
+
178
+ it ( 'should run validation onChange' , ( ) => {
155
179
const form = new FormApi ( {
156
180
defaultValues : {
157
181
name : 'test' ,
@@ -162,10 +186,33 @@ describe('field api', () => {
162
186
form,
163
187
name : 'name' ,
164
188
onChange : ( value ) => {
165
- if ( value === 'other' ) {
166
- return 'Please enter a different value'
167
- }
189
+ if ( value === 'other' ) return 'Please enter a different value'
190
+ return
191
+ } ,
192
+ } )
193
+
194
+ field . mount ( )
195
+
196
+ expect ( field . getMeta ( ) . error ) . toBeUndefined ( )
197
+ field . setValue ( 'other' , { touch : true } )
198
+ expect ( field . getMeta ( ) . error ) . toBe ( 'Please enter a different value' )
199
+ } )
200
+
201
+ it ( 'should run async validation onChange' , async ( ) => {
202
+ vi . useFakeTimers ( )
168
203
204
+ const form = new FormApi ( {
205
+ defaultValues : {
206
+ name : 'test' ,
207
+ } ,
208
+ } )
209
+
210
+ const field = new FieldApi ( {
211
+ form,
212
+ name : 'name' ,
213
+ onChangeAsync : async ( value ) => {
214
+ await sleep ( 1000 )
215
+ if ( value === 'other' ) return 'Please enter a different value'
169
216
return
170
217
} ,
171
218
} )
@@ -174,10 +221,14 @@ describe('field api', () => {
174
221
175
222
expect ( field . getMeta ( ) . error ) . toBeUndefined ( )
176
223
field . setValue ( 'other' , { touch : true } )
224
+ await vi . runAllTimersAsync ( )
177
225
expect ( field . getMeta ( ) . error ) . toBe ( 'Please enter a different value' )
178
226
} )
179
227
180
- it ( 'should not throw errors when no meta info is stored on a field and a form re-renders' , async ( ) => {
228
+ it ( 'should run async validation onChange with debounce' , async ( ) => {
229
+ vi . useFakeTimers ( )
230
+ const sleepMock = vi . fn ( ) . mockImplementation ( sleep )
231
+
181
232
const form = new FormApi ( {
182
233
defaultValues : {
183
234
name : 'test' ,
@@ -187,16 +238,199 @@ describe('field api', () => {
187
238
const field = new FieldApi ( {
188
239
form,
189
240
name : 'name' ,
241
+ onChangeAsyncDebounceMs : 1000 ,
242
+ onChangeAsync : async ( value ) => {
243
+ await sleepMock ( 1000 )
244
+ if ( value === 'other' ) return 'Please enter a different value'
245
+ return
246
+ } ,
190
247
} )
191
248
192
249
field . mount ( )
193
250
194
- expect ( ( ) =>
195
- form . update ( {
196
- defaultValues : {
197
- name : 'other' ,
198
- } ,
199
- } ) ,
200
- ) . not . toThrow ( )
251
+ expect ( field . getMeta ( ) . error ) . toBeUndefined ( )
252
+ field . setValue ( 'other' , { touch : true } )
253
+ field . setValue ( 'other' )
254
+ await vi . runAllTimersAsync ( )
255
+ // sleepMock will have been called 2 times without onChangeAsyncDebounceMs
256
+ expect ( sleepMock ) . toHaveBeenCalledTimes ( 1 )
257
+ expect ( field . getMeta ( ) . error ) . toBe ( 'Please enter a different value' )
258
+ } )
259
+
260
+ it ( 'should run async validation onChange with asyncDebounceMs' , async ( ) => {
261
+ vi . useFakeTimers ( )
262
+ const sleepMock = vi . fn ( ) . mockImplementation ( sleep )
263
+
264
+ const form = new FormApi ( {
265
+ defaultValues : {
266
+ name : 'test' ,
267
+ } ,
268
+ } )
269
+
270
+ const field = new FieldApi ( {
271
+ form,
272
+ name : 'name' ,
273
+ asyncDebounceMs : 1000 ,
274
+ onChangeAsync : async ( value ) => {
275
+ await sleepMock ( 1000 )
276
+ if ( value === 'other' ) return 'Please enter a different value'
277
+ return
278
+ } ,
279
+ } )
280
+
281
+ field . mount ( )
282
+
283
+ expect ( field . getMeta ( ) . error ) . toBeUndefined ( )
284
+ field . setValue ( 'other' , { touch : true } )
285
+ field . setValue ( 'other' )
286
+ await vi . runAllTimersAsync ( )
287
+ // sleepMock will have been called 2 times without asyncDebounceMs
288
+ expect ( sleepMock ) . toHaveBeenCalledTimes ( 1 )
289
+ expect ( field . getMeta ( ) . error ) . toBe ( 'Please enter a different value' )
290
+ } )
291
+
292
+ it ( 'should run validation onBlur' , ( ) => {
293
+ const form = new FormApi ( {
294
+ defaultValues : {
295
+ name : 'other' ,
296
+ } ,
297
+ } )
298
+
299
+ const field = new FieldApi ( {
300
+ form,
301
+ name : 'name' ,
302
+ onBlur : ( value ) => {
303
+ if ( value === 'other' ) return 'Please enter a different value'
304
+ return
305
+ } ,
306
+ } )
307
+
308
+ field . mount ( )
309
+
310
+ field . setValue ( 'other' , { touch : true } )
311
+ field . validate ( 'blur' )
312
+ expect ( field . getMeta ( ) . error ) . toBe ( 'Please enter a different value' )
313
+ } )
314
+
315
+ it ( 'should run async validation onBlur' , async ( ) => {
316
+ vi . useFakeTimers ( )
317
+
318
+ const form = new FormApi ( {
319
+ defaultValues : {
320
+ name : 'test' ,
321
+ } ,
322
+ } )
323
+
324
+ const field = new FieldApi ( {
325
+ form,
326
+ name : 'name' ,
327
+ onBlurAsync : async ( value ) => {
328
+ await sleep ( 1000 )
329
+ if ( value === 'other' ) return 'Please enter a different value'
330
+ return
331
+ } ,
332
+ } )
333
+
334
+ field . mount ( )
335
+
336
+ expect ( field . getMeta ( ) . error ) . toBeUndefined ( )
337
+ field . setValue ( 'other' , { touch : true } )
338
+ field . validate ( 'blur' )
339
+ await vi . runAllTimersAsync ( )
340
+ expect ( field . getMeta ( ) . error ) . toBe ( 'Please enter a different value' )
341
+ } )
342
+
343
+ it ( 'should run async validation onBlur with debounce' , async ( ) => {
344
+ vi . useFakeTimers ( )
345
+ const sleepMock = vi . fn ( ) . mockImplementation ( sleep )
346
+
347
+ const form = new FormApi ( {
348
+ defaultValues : {
349
+ name : 'test' ,
350
+ } ,
351
+ } )
352
+
353
+ const field = new FieldApi ( {
354
+ form,
355
+ name : 'name' ,
356
+ onBlurAsyncDebounceMs : 1000 ,
357
+ onBlurAsync : async ( value ) => {
358
+ await sleepMock ( 10 )
359
+ if ( value === 'other' ) return 'Please enter a different value'
360
+ return
361
+ } ,
362
+ } )
363
+
364
+ field . mount ( )
365
+
366
+ expect ( field . getMeta ( ) . error ) . toBeUndefined ( )
367
+ field . setValue ( 'other' , { touch : true } )
368
+ field . validate ( 'blur' )
369
+ field . validate ( 'blur' )
370
+ await vi . runAllTimersAsync ( )
371
+ // sleepMock will have been called 2 times without onBlurAsyncDebounceMs
372
+ expect ( sleepMock ) . toHaveBeenCalledTimes ( 1 )
373
+ expect ( field . getMeta ( ) . error ) . toBe ( 'Please enter a different value' )
374
+ } )
375
+
376
+ it ( 'should run async validation onBlur with asyncDebounceMs' , async ( ) => {
377
+ vi . useFakeTimers ( )
378
+ const sleepMock = vi . fn ( ) . mockImplementation ( sleep )
379
+
380
+ const form = new FormApi ( {
381
+ defaultValues : {
382
+ name : 'test' ,
383
+ } ,
384
+ } )
385
+
386
+ const field = new FieldApi ( {
387
+ form,
388
+ name : 'name' ,
389
+ asyncDebounceMs : 1000 ,
390
+ onBlurAsync : async ( value ) => {
391
+ await sleepMock ( 10 )
392
+ if ( value === 'other' ) return 'Please enter a different value'
393
+ return
394
+ } ,
395
+ } )
396
+
397
+ field . mount ( )
398
+
399
+ expect ( field . getMeta ( ) . error ) . toBeUndefined ( )
400
+ field . setValue ( 'other' , { touch : true } )
401
+ field . validate ( 'blur' )
402
+ field . validate ( 'blur' )
403
+ await vi . runAllTimersAsync ( )
404
+ // sleepMock will have been called 2 times without asyncDebounceMs
405
+ expect ( sleepMock ) . toHaveBeenCalledTimes ( 1 )
406
+ expect ( field . getMeta ( ) . error ) . toBe ( 'Please enter a different value' )
407
+ } )
408
+
409
+ it ( 'should run async validation onSubmit' , async ( ) => {
410
+ vi . useFakeTimers ( )
411
+
412
+ const form = new FormApi ( {
413
+ defaultValues : {
414
+ name : 'test' ,
415
+ } ,
416
+ } )
417
+
418
+ const field = new FieldApi ( {
419
+ form,
420
+ name : 'name' ,
421
+ onSubmitAsync : async ( value ) => {
422
+ await sleep ( 1000 )
423
+ if ( value === 'other' ) return 'Please enter a different value'
424
+ return
425
+ } ,
426
+ } )
427
+
428
+ field . mount ( )
429
+
430
+ expect ( field . getMeta ( ) . error ) . toBeUndefined ( )
431
+ field . setValue ( 'other' , { touch : true } )
432
+ field . validate ( 'submit' )
433
+ await vi . runAllTimersAsync ( )
434
+ expect ( field . getMeta ( ) . error ) . toBe ( 'Please enter a different value' )
201
435
} )
202
436
} )
0 commit comments