@@ -838,4 +838,205 @@ describe('createRouter', { concurrent: true }, function () {
838838 expect ( router . match ( '/dashboard/settings' ) ?. handler . component ) . toBe ( DashSettings )
839839 } )
840840 } )
841+
842+ describe ( 'redirect cycle detection' , { concurrent : true } , function ( ) {
843+ it ( 'throws on a self-redirect' , function ( { expect } ) {
844+ expect ( function ( ) {
845+ createRouter ( function ( route ) {
846+ route ( '/loop' ) . redirect ( '/loop' )
847+ } )
848+ } ) . toThrow ( 'redirect cycle detected' )
849+ } )
850+
851+ it ( 'throws on a two-hop redirect cycle' , function ( { expect } ) {
852+ expect ( function ( ) {
853+ createRouter ( function ( route ) {
854+ route ( '/a' ) . redirect ( '/b' )
855+ route ( '/b' ) . redirect ( '/a' )
856+ } )
857+ } ) . toThrow ( 'redirect cycle detected' )
858+ } )
859+
860+ it ( 'throws on a multi-hop redirect cycle' , function ( { expect } ) {
861+ expect ( function ( ) {
862+ createRouter ( function ( route ) {
863+ route ( '/a' ) . redirect ( '/b' )
864+ route ( '/b' ) . redirect ( '/c' )
865+ route ( '/c' ) . redirect ( '/a' )
866+ } )
867+ } ) . toThrow ( 'redirect cycle detected' )
868+ } )
869+
870+ it ( 'allows non-cyclic redirect chains' , function ( { expect } ) {
871+ expect ( function ( ) {
872+ createRouter ( function ( route ) {
873+ route ( '/a' ) . redirect ( '/b' )
874+ route ( '/b' ) . redirect ( '/c' )
875+ route ( '/c' ) . render ( Stub )
876+ } )
877+ } ) . not . toThrow ( )
878+ } )
879+
880+ it ( 'skips cycle detection for callback redirects' , function ( { expect } ) {
881+ expect ( function ( ) {
882+ createRouter ( function ( route ) {
883+ route ( '/a' ) . redirect ( function ( ) {
884+ return '/a'
885+ } )
886+ } )
887+ } ) . not . toThrow ( )
888+ } )
889+ } )
890+
891+ describe ( 'builder consumed guard' , { concurrent : true } , function ( ) {
892+ it ( 'throws when calling middleware after render' , function ( { expect } ) {
893+ expect ( function ( ) {
894+ createRouter ( function ( route ) {
895+ const builder = route ( '/page' )
896+
897+ builder . render ( Stub )
898+ builder . middleware ( [ createMiddleware ( ) ] )
899+ } )
900+ } ) . toThrow ( 'cannot call .middleware() on a route builder' )
901+ } )
902+
903+ it ( 'throws when calling prefetch after render' , function ( { expect } ) {
904+ expect ( function ( ) {
905+ createRouter ( function ( route ) {
906+ const builder = route ( '/page' )
907+
908+ builder . render ( Stub )
909+ builder . prefetch ( vi . fn ( ) )
910+ } )
911+ } ) . toThrow ( 'cannot call .prefetch() on a route builder' )
912+ } )
913+
914+ it ( 'throws when calling scroll after render' , function ( { expect } ) {
915+ expect ( function ( ) {
916+ createRouter ( function ( route ) {
917+ const builder = route ( '/page' )
918+
919+ builder . render ( Stub )
920+ builder . scroll ( 'manual' )
921+ } )
922+ } ) . toThrow ( 'cannot call .scroll() on a route builder' )
923+ } )
924+
925+ it ( 'throws when calling focusReset after render' , function ( { expect } ) {
926+ expect ( function ( ) {
927+ createRouter ( function ( route ) {
928+ const builder = route ( '/page' )
929+
930+ builder . render ( Stub )
931+ builder . focusReset ( 'manual' )
932+ } )
933+ } ) . toThrow ( 'cannot call .focusReset() on a route builder' )
934+ } )
935+
936+ it ( 'throws when calling formHandler after render' , function ( { expect } ) {
937+ expect ( function ( ) {
938+ createRouter ( function ( route ) {
939+ const builder = route ( '/page' )
940+
941+ builder . render ( Stub )
942+ builder . formHandler ( vi . fn ( ) )
943+ } )
944+ } ) . toThrow ( 'cannot call .formHandler() on a route builder' )
945+ } )
946+
947+ it ( 'throws when calling render twice' , function ( { expect } ) {
948+ expect ( function ( ) {
949+ createRouter ( function ( route ) {
950+ const builder = route ( '/page' )
951+
952+ builder . render ( Stub )
953+ builder . render ( createStub ( ) )
954+ } )
955+ } ) . toThrow ( 'cannot call .render() on a route builder' )
956+ } )
957+
958+ it ( 'throws when calling redirect after render' , function ( { expect } ) {
959+ expect ( function ( ) {
960+ createRouter ( function ( route ) {
961+ const builder = route ( '/page' )
962+
963+ builder . render ( Stub )
964+ builder . redirect ( '/other' )
965+ } )
966+ } ) . toThrow ( 'cannot call .redirect() on a route builder' )
967+ } )
968+
969+ it ( 'throws when calling group after render' , function ( { expect } ) {
970+ expect ( function ( ) {
971+ createRouter ( function ( route ) {
972+ const builder = route ( '/page' )
973+
974+ builder . render ( Stub )
975+ builder . group ( )
976+ } )
977+ } ) . toThrow ( 'cannot call .group() on a route builder' )
978+ } )
979+
980+ it ( 'throws when calling render after redirect' , function ( { expect } ) {
981+ expect ( function ( ) {
982+ createRouter ( function ( route ) {
983+ const builder = route ( '/page' )
984+
985+ builder . redirect ( '/other' )
986+ builder . render ( Stub )
987+ } )
988+ } ) . toThrow ( 'cannot call .render() on a route builder' )
989+ } )
990+
991+ it ( 'throws when calling render after group' , function ( { expect } ) {
992+ expect ( function ( ) {
993+ createRouter ( function ( route ) {
994+ const builder = route ( '/page' )
995+
996+ builder . group ( )
997+ builder . render ( Stub )
998+ } )
999+ } ) . toThrow ( 'cannot call .render() on a route builder' )
1000+ } )
1001+ } )
1002+
1003+ describe ( 'duplicate route registration' , { concurrent : true } , function ( ) {
1004+ it ( 'throws when registering the same path twice with render' , function ( { expect } ) {
1005+ expect ( function ( ) {
1006+ createRouter ( function ( route ) {
1007+ route ( '/page' ) . render ( Stub )
1008+ route ( '/page' ) . render ( createStub ( ) )
1009+ } )
1010+ } ) . toThrow ( 'duplicate route registration' )
1011+ } )
1012+
1013+ it ( 'throws when registering the same path with render and redirect' , function ( { expect } ) {
1014+ expect ( function ( ) {
1015+ createRouter ( function ( route ) {
1016+ route ( '/page' ) . render ( Stub )
1017+ route ( '/page' ) . redirect ( '/other' )
1018+ } )
1019+ } ) . toThrow ( 'duplicate route registration' )
1020+ } )
1021+
1022+ it ( 'throws when registering the same nested path twice' , function ( { expect } ) {
1023+ expect ( function ( ) {
1024+ createRouter ( function ( route ) {
1025+ const app = route ( '/app' ) . group ( )
1026+
1027+ app ( '/page' ) . render ( Stub )
1028+ app ( '/page' ) . render ( createStub ( ) )
1029+ } )
1030+ } ) . toThrow ( 'duplicate route registration' )
1031+ } )
1032+
1033+ it ( 'allows registering different paths' , function ( { expect } ) {
1034+ expect ( function ( ) {
1035+ createRouter ( function ( route ) {
1036+ route ( '/a' ) . render ( Stub )
1037+ route ( '/b' ) . render ( createStub ( ) )
1038+ } )
1039+ } ) . not . toThrow ( )
1040+ } )
1041+ } )
8411042} )
0 commit comments