@@ -11,6 +11,8 @@ import { RequestHandlerContext, useInfiniteModelQuery, useModelMutation, useMode
11
11
import { getQueryKey } from '../src/runtime/common' ;
12
12
import { modelMeta } from './test-model-meta' ;
13
13
14
+ const BASE_URL = 'http://localhost' ;
15
+
14
16
describe ( 'Tanstack Query React Hooks V5 Test' , ( ) => {
15
17
function createWrapper ( ) {
16
18
const queryClient = new QueryClient ( ) ;
@@ -25,7 +27,7 @@ describe('Tanstack Query React Hooks V5 Test', () => {
25
27
}
26
28
27
29
function makeUrl ( model : string , operation : string , args ?: unknown ) {
28
- let r = `http://localhost /api/model/${ model } /${ operation } ` ;
30
+ let r = `${ BASE_URL } /api/model/${ model } /${ operation } ` ;
29
31
if ( args ) {
30
32
r += `?q=${ encodeURIComponent ( JSON . stringify ( args ) ) } ` ;
31
33
}
@@ -345,6 +347,350 @@ describe('Tanstack Query React Hooks V5 Test', () => {
345
347
} ) ;
346
348
} ) ;
347
349
350
+ it ( 'optimistic create updating deeply nested query' , async ( ) => {
351
+ const { queryClient, wrapper } = createWrapper ( ) ;
352
+
353
+ // populate the cache with a user
354
+
355
+ const userData : any [ ] = [ { id : '1' , name : 'user1' , posts : [ ] } ] ;
356
+
357
+ nock ( BASE_URL )
358
+ . get ( '/api/model/User/findMany' )
359
+ . query ( true )
360
+ . reply ( 200 , ( ) => {
361
+ console . log ( 'Querying data:' , JSON . stringify ( userData ) ) ;
362
+ return { data : userData } ;
363
+ } )
364
+ . persist ( ) ;
365
+
366
+ const { result : userResult } = renderHook (
367
+ ( ) =>
368
+ useModelQuery (
369
+ 'User' ,
370
+ makeUrl ( 'User' , 'findMany' ) ,
371
+ {
372
+ include : {
373
+ posts : {
374
+ include : {
375
+ category : true ,
376
+ } ,
377
+ } ,
378
+ } ,
379
+ } ,
380
+ { optimisticUpdate : true }
381
+ ) ,
382
+ {
383
+ wrapper,
384
+ }
385
+ ) ;
386
+ await waitFor ( ( ) => {
387
+ expect ( userResult . current . data ) . toHaveLength ( 1 ) ;
388
+ } ) ;
389
+
390
+ // pupulate the cache with a category
391
+ const categoryData : any [ ] = [ { id : '1' , name : 'category1' , posts : [ ] } ] ;
392
+
393
+ nock ( BASE_URL )
394
+ . get ( '/api/model/Category/findMany' )
395
+ . query ( true )
396
+ . reply ( 200 , ( ) => {
397
+ console . log ( 'Querying data:' , JSON . stringify ( categoryData ) ) ;
398
+ return { data : categoryData } ;
399
+ } )
400
+ . persist ( ) ;
401
+
402
+ const { result : categoryResult } = renderHook (
403
+ ( ) =>
404
+ useModelQuery (
405
+ 'Category' ,
406
+ makeUrl ( 'Category' , 'findMany' ) ,
407
+ { include : { posts : true } } ,
408
+ { optimisticUpdate : true }
409
+ ) ,
410
+ {
411
+ wrapper,
412
+ }
413
+ ) ;
414
+ await waitFor ( ( ) => {
415
+ expect ( categoryResult . current . data ) . toHaveLength ( 1 ) ;
416
+ } ) ;
417
+
418
+ // create a post and connect it to the category
419
+ nock ( BASE_URL )
420
+ . post ( '/api/model/Post/create' )
421
+ . reply ( 200 , ( ) => {
422
+ console . log ( 'Not mutating data' ) ;
423
+ return { data : null } ;
424
+ } ) ;
425
+
426
+ const { result : mutationResult } = renderHook (
427
+ ( ) =>
428
+ useModelMutation ( 'Post' , 'POST' , makeUrl ( 'Post' , 'create' ) , modelMeta , {
429
+ optimisticUpdate : true ,
430
+ invalidateQueries : false ,
431
+ } ) ,
432
+ {
433
+ wrapper,
434
+ }
435
+ ) ;
436
+
437
+ act ( ( ) =>
438
+ mutationResult . current . mutate ( {
439
+ data : { title : 'post1' , owner : { connect : { id : '1' } } , category : { connect : { id : '1' } } } ,
440
+ } )
441
+ ) ;
442
+
443
+ // assert that the post was created and connected to the category
444
+ await waitFor ( ( ) => {
445
+ const cacheData : any = queryClient . getQueryData (
446
+ getQueryKey (
447
+ 'Category' ,
448
+ 'findMany' ,
449
+ {
450
+ include : {
451
+ posts : true ,
452
+ } ,
453
+ } ,
454
+ { infinite : false , optimisticUpdate : true }
455
+ )
456
+ ) ;
457
+ const posts = cacheData [ 0 ] . posts ;
458
+ expect ( posts ) . toHaveLength ( 1 ) ;
459
+ console . log ( 'category.posts' , posts [ 0 ] ) ;
460
+ expect ( posts [ 0 ] ) . toMatchObject ( {
461
+ $optimistic : true ,
462
+ id : expect . any ( String ) ,
463
+ title : 'post1' ,
464
+ ownerId : '1' ,
465
+ } ) ;
466
+ } ) ;
467
+
468
+ // assert that the post was created and connected to the user, and included the category
469
+ await waitFor ( ( ) => {
470
+ const cacheData : any = queryClient . getQueryData (
471
+ getQueryKey (
472
+ 'User' ,
473
+ 'findMany' ,
474
+ {
475
+ include : {
476
+ posts : {
477
+ include : {
478
+ category : true ,
479
+ } ,
480
+ } ,
481
+ } ,
482
+ } ,
483
+ { infinite : false , optimisticUpdate : true }
484
+ )
485
+ ) ;
486
+ const posts = cacheData [ 0 ] . posts ;
487
+ expect ( posts ) . toHaveLength ( 1 ) ;
488
+ console . log ( 'user.posts' , posts [ 0 ] ) ;
489
+ expect ( posts [ 0 ] ) . toMatchObject ( {
490
+ $optimistic : true ,
491
+ id : expect . any ( String ) ,
492
+ title : 'post1' ,
493
+ ownerId : '1' ,
494
+ categoryId : '1' ,
495
+ // TODO: should this include the category object and not just the foreign key?
496
+ // category: { $optimistic: true, id: '1', name: 'category1' },
497
+ } ) ;
498
+ } ) ;
499
+ } ) ;
500
+
501
+ it ( 'optimistic update with optional one-to-many relationship' , async ( ) => {
502
+ const { queryClient, wrapper } = createWrapper ( ) ;
503
+
504
+ // populate the cache with a post, with an optional category relatonship
505
+ const postData : any = {
506
+ id : '1' ,
507
+ title : 'post1' ,
508
+ ownerId : '1' ,
509
+ categoryId : null ,
510
+ category : null ,
511
+ } ;
512
+
513
+ const data : any [ ] = [ postData ] ;
514
+
515
+ nock ( makeUrl ( 'Post' , 'findMany' ) )
516
+ . get ( / .* / )
517
+ . query ( true )
518
+ . reply ( 200 , ( ) => {
519
+ console . log ( 'Querying data:' , JSON . stringify ( data ) ) ;
520
+ return { data } ;
521
+ } )
522
+ . persist ( ) ;
523
+
524
+ const { result : postResult } = renderHook (
525
+ ( ) =>
526
+ useModelQuery (
527
+ 'Post' ,
528
+ makeUrl ( 'Post' , 'findMany' ) ,
529
+ {
530
+ include : {
531
+ category : true ,
532
+ } ,
533
+ } ,
534
+ { optimisticUpdate : true }
535
+ ) ,
536
+ {
537
+ wrapper,
538
+ }
539
+ ) ;
540
+ await waitFor ( ( ) => {
541
+ expect ( postResult . current . data ) . toHaveLength ( 1 ) ;
542
+ } ) ;
543
+
544
+ // mock a put request to update the post title
545
+ nock ( makeUrl ( 'Post' , 'update' ) )
546
+ . put ( / .* / )
547
+ . reply ( 200 , ( ) => {
548
+ console . log ( 'Mutating data' ) ;
549
+ postData . title = 'postA' ;
550
+ return { data : postData } ;
551
+ } ) ;
552
+
553
+ const { result : mutationResult } = renderHook (
554
+ ( ) =>
555
+ useModelMutation ( 'Post' , 'PUT' , makeUrl ( 'Post' , 'update' ) , modelMeta , {
556
+ optimisticUpdate : true ,
557
+ invalidateQueries : false ,
558
+ } ) ,
559
+ {
560
+ wrapper,
561
+ }
562
+ ) ;
563
+
564
+ act ( ( ) => mutationResult . current . mutate ( { where : { id : '1' } , data : { title : 'postA' } } ) ) ;
565
+
566
+ // assert that the post was updated despite the optional (null) category relationship
567
+ await waitFor ( ( ) => {
568
+ const cacheData : any = queryClient . getQueryData (
569
+ getQueryKey (
570
+ 'Post' ,
571
+ 'findMany' ,
572
+ {
573
+ include : {
574
+ category : true ,
575
+ } ,
576
+ } ,
577
+ { infinite : false , optimisticUpdate : true }
578
+ )
579
+ ) ;
580
+ const posts = cacheData ;
581
+ expect ( posts ) . toHaveLength ( 1 ) ;
582
+ expect ( posts [ 0 ] ) . toMatchObject ( {
583
+ $optimistic : true ,
584
+ id : expect . any ( String ) ,
585
+ title : 'postA' ,
586
+ ownerId : '1' ,
587
+ categoryId : null ,
588
+ category : null ,
589
+ } ) ;
590
+ } ) ;
591
+ } ) ;
592
+
593
+ it ( 'optimistic update with nested optional one-to-many relationship' , async ( ) => {
594
+ const { queryClient, wrapper } = createWrapper ( ) ;
595
+
596
+ // populate the cache with a user and a post, with an optional category
597
+ const postData : any = {
598
+ id : '1' ,
599
+ title : 'post1' ,
600
+ ownerId : '1' ,
601
+ categoryId : null ,
602
+ category : null ,
603
+ } ;
604
+
605
+ const userData : any [ ] = [ { id : '1' , name : 'user1' , posts : [ postData ] } ] ;
606
+
607
+ nock ( BASE_URL )
608
+ . get ( '/api/model/User/findMany' )
609
+ . query ( true )
610
+ . reply ( 200 , ( ) => {
611
+ console . log ( 'Querying data:' , JSON . stringify ( userData ) ) ;
612
+ return { data : userData } ;
613
+ } )
614
+ . persist ( ) ;
615
+
616
+ const { result : userResult } = renderHook (
617
+ ( ) =>
618
+ useModelQuery (
619
+ 'User' ,
620
+ makeUrl ( 'User' , 'findMany' ) ,
621
+ {
622
+ include : {
623
+ posts : {
624
+ include : {
625
+ category : true ,
626
+ } ,
627
+ } ,
628
+ } ,
629
+ } ,
630
+ { optimisticUpdate : true }
631
+ ) ,
632
+ {
633
+ wrapper,
634
+ }
635
+ ) ;
636
+ await waitFor ( ( ) => {
637
+ expect ( userResult . current . data ) . toHaveLength ( 1 ) ;
638
+ } ) ;
639
+
640
+ // mock a put request to update the post title
641
+ nock ( BASE_URL )
642
+ . put ( '/api/model/Post/update' )
643
+ . reply ( 200 , ( ) => {
644
+ console . log ( 'Mutating data' ) ;
645
+ postData . title = 'postA' ;
646
+ return { data : postData } ;
647
+ } ) ;
648
+
649
+ const { result : mutationResult } = renderHook (
650
+ ( ) =>
651
+ useModelMutation ( 'Post' , 'PUT' , makeUrl ( 'Post' , 'update' ) , modelMeta , {
652
+ optimisticUpdate : true ,
653
+ invalidateQueries : false ,
654
+ } ) ,
655
+ {
656
+ wrapper,
657
+ }
658
+ ) ;
659
+
660
+ act ( ( ) => mutationResult . current . mutate ( { where : { id : '1' } , data : { title : 'postA' } } ) ) ;
661
+
662
+ // assert that the post was updated
663
+ await waitFor ( ( ) => {
664
+ const cacheData : any = queryClient . getQueryData (
665
+ getQueryKey (
666
+ 'User' ,
667
+ 'findMany' ,
668
+ {
669
+ include : {
670
+ posts : {
671
+ include : {
672
+ category : true ,
673
+ } ,
674
+ } ,
675
+ } ,
676
+ } ,
677
+ { infinite : false , optimisticUpdate : true }
678
+ )
679
+ ) ;
680
+ const posts = cacheData [ 0 ] . posts ;
681
+ expect ( posts ) . toHaveLength ( 1 ) ;
682
+ console . log ( 'user.posts' , posts [ 0 ] ) ;
683
+ expect ( posts [ 0 ] ) . toMatchObject ( {
684
+ $optimistic : true ,
685
+ id : expect . any ( String ) ,
686
+ title : 'postA' ,
687
+ ownerId : '1' ,
688
+ categoryId : null ,
689
+ category : null ,
690
+ } ) ;
691
+ } ) ;
692
+ } ) ;
693
+
348
694
it ( 'optimistic nested create updating query' , async ( ) => {
349
695
const { queryClient, wrapper } = createWrapper ( ) ;
350
696
0 commit comments