@@ -626,4 +626,165 @@ describe('createQueryParamStore', () => {
626
626
expect ( result2 . current [ 0 ] ) . toEqual ( { param2 : 'value2' } )
627
627
expect ( mockRouter . query ) . toEqual ( { param1 : 'value1' , param2 : 'value2' } )
628
628
} )
629
+
630
+ it ( 'correctly synchronizes initial SSR state with client state' , async ( ) => {
631
+ // Arrange
632
+ const schemaWithDefault = z . object ( {
633
+ status : z . string ( ) . default ( 'all' ) ,
634
+ search : z . string ( ) . optional ( ) ,
635
+ } )
636
+ const { useQueryParams } = createQueryParamStore ( schemaWithDefault )
637
+
638
+ // Simulate SSR by setting initial router state
639
+ await mockRouter . push ( { query : { status : 'draft' } } )
640
+
641
+ // Act
642
+ const { result } = renderHook ( ( ) => useQueryParams ( ) , {
643
+ wrapper : MemoryRouterProvider ,
644
+ } )
645
+
646
+ // Assert
647
+ await waitFor ( ( ) => {
648
+ // Changed expectation to match the schema definition
649
+ expect ( result . current [ 0 ] ) . toEqual ( { status : 'draft' } )
650
+ expect ( mockRouter . query . status ) . toBe ( 'draft' )
651
+ } )
652
+ } )
653
+
654
+ it ( 'maintains router query value over default value during initialization' , async ( ) => {
655
+ // Arrange
656
+ const schemaWithDefault = z . object ( {
657
+ status : z . string ( ) . default ( 'all' ) ,
658
+ } )
659
+ const { useQueryParams } = createQueryParamStore ( schemaWithDefault )
660
+
661
+ // Set initial router state
662
+ await mockRouter . push ( { query : { status : 'draft' } } )
663
+
664
+ // Act
665
+ const { result } = renderHook ( ( ) => useQueryParams ( ) , {
666
+ wrapper : MemoryRouterProvider ,
667
+ } )
668
+
669
+ // Assert
670
+ await waitFor ( ( ) => {
671
+ expect ( result . current [ 0 ] . status ) . toBe ( 'draft' )
672
+ } )
673
+
674
+ // Update the value
675
+ act ( ( ) => {
676
+ result . current [ 1 ] ( { status : 'sent' } )
677
+ } )
678
+
679
+ // Assert the update worked
680
+ await waitFor ( ( ) => {
681
+ expect ( result . current [ 0 ] . status ) . toBe ( 'sent' )
682
+ } )
683
+ } )
684
+
685
+ it ( 'handles initialization order correctly with multiple sources' , async ( ) => {
686
+ // Arrange
687
+ const customSchema = z . object ( {
688
+ status : z . string ( ) . default ( 'all' ) ,
689
+ search : z . string ( ) . optional ( ) ,
690
+ } )
691
+
692
+ const { useQueryParams } = createQueryParamStore ( customSchema )
693
+
694
+ // Set up different values from different sources
695
+ const initialQuery = { status : 'initial' } as ParsedUrlQuery
696
+ await mockRouter . push ( { query : { status : 'router' } } )
697
+
698
+ // Act
699
+ const { result } = renderHook ( ( ) => useQueryParams ( initialQuery ) , {
700
+ wrapper : MemoryRouterProvider ,
701
+ } )
702
+
703
+ // Assert - router query should take precedence
704
+ await waitFor ( ( ) => {
705
+ expect ( result . current [ 0 ] . status ) . toBe ( 'router' )
706
+ } )
707
+ } )
708
+
709
+ it ( 'preserves SSR values during hydration' , async ( ) => {
710
+ // Arrange
711
+ const customSchema = z . object ( {
712
+ status : z . string ( ) ,
713
+ search : z . string ( ) . optional ( ) ,
714
+ } )
715
+ const { useQueryParams } = createQueryParamStore ( customSchema )
716
+ const ssrQuery = { status : 'sent' , search : '' }
717
+
718
+ // Act
719
+ const { result } = renderHook ( ( ) => useQueryParams ( ssrQuery ) , {
720
+ wrapper : MemoryRouterProvider ,
721
+ } )
722
+
723
+ // Assert
724
+ expect ( result . current [ 0 ] ) . toEqual ( ssrQuery )
725
+
726
+ // Simulate client-side navigation
727
+ await act ( async ( ) => {
728
+ await mockRouter . push ( { query : { status : 'draft' } } )
729
+ } )
730
+
731
+ // Check that new values are reflected
732
+ await waitFor ( ( ) => {
733
+ expect ( result . current [ 0 ] ) . toEqual ( { status : 'draft' , search : '' } )
734
+ } )
735
+ } )
736
+
737
+ it ( 'handles redirect scenarios correctly' , async ( ) => {
738
+ // Arrange
739
+ const customSchema = z . object ( {
740
+ status : z . string ( ) ,
741
+ page : z . string ( ) . optional ( ) ,
742
+ } )
743
+ const { useQueryParams } = createQueryParamStore ( customSchema )
744
+ const initialQuery = { status : 'initial' }
745
+
746
+ // Act - simulate a redirect scenario
747
+ const { result, rerender } = renderHook ( ( ) => useQueryParams ( initialQuery ) , {
748
+ wrapper : MemoryRouterProvider ,
749
+ } )
750
+
751
+ // Simulate redirect by changing router query
752
+ await act ( async ( ) => {
753
+ await mockRouter . push ( { query : { status : 'redirected' , page : '2' } } )
754
+ } )
755
+
756
+ // Force rerender to simulate redirect completion
757
+ rerender ( )
758
+
759
+ // Assert
760
+ await waitFor ( ( ) => {
761
+ expect ( result . current [ 0 ] ) . toEqual ( { status : 'redirected' , page : '2' } )
762
+ } )
763
+ } )
764
+
765
+ it ( 'maintains query param order during initialization' , async ( ) => {
766
+ // Arrange
767
+ const customSchema = z . object ( {
768
+ status : z . string ( ) . default ( 'all' ) ,
769
+ search : z . string ( ) . optional ( ) ,
770
+ } )
771
+ const { useQueryParams } = createQueryParamStore ( customSchema )
772
+
773
+ // Set up competing values
774
+ const ssrQuery = { status : 'ssr' , search : 'ssr-search' }
775
+ await mockRouter . push ( { query : { status : 'client' , search : 'client-search' } } )
776
+
777
+ // Act
778
+ const { result } = renderHook ( ( ) => useQueryParams ( ssrQuery ) , {
779
+ wrapper : MemoryRouterProvider ,
780
+ } )
781
+
782
+ // Assert - client values should take precedence
783
+ await waitFor ( ( ) => {
784
+ expect ( result . current [ 0 ] ) . toEqual ( {
785
+ status : 'client' ,
786
+ search : 'client-search' ,
787
+ } )
788
+ } )
789
+ } )
629
790
} )
0 commit comments