@@ -12,13 +12,18 @@ import {
1212import type { Location , Params } from "react-router-dom" ;
1313import {
1414 BrowserRouter ,
15+ HashRouter ,
16+ MemoryRouter ,
1517 Link ,
1618 Routes ,
1719 Route ,
1820 RouterProvider ,
1921 createBrowserRouter ,
22+ createHashRouter ,
23+ createMemoryRouter ,
2024 createRoutesFromElements ,
2125 useLocation ,
26+ useNavigate ,
2227 useParams ,
2328} from "react-router-dom" ;
2429
@@ -709,4 +714,272 @@ describe("special character tests", () => {
709714 }
710715 } ) ;
711716 } ) ;
717+
718+ describe ( "encodes characters based on history implementation" , ( ) => {
719+ function ShowPath ( ) {
720+ let { pathname, search, hash } = useLocation ( ) ;
721+ return < pre > { JSON . stringify ( { pathname, search, hash } ) } </ pre > ;
722+ }
723+
724+ describe ( "memory routers" , ( ) => {
725+ it ( "does not encode characters in MemoryRouter" , ( ) => {
726+ let ctx = render (
727+ < MemoryRouter initialEntries = { [ "/with space" ] } >
728+ < Routes >
729+ < Route path = "/with space" element = { < ShowPath /> } />
730+ </ Routes >
731+ </ MemoryRouter >
732+ ) ;
733+
734+ expect ( ctx . container . innerHTML ) . toMatchInlineSnapshot (
735+ `"<pre>{\\"pathname\\":\\"/with space\\",\\"search\\":\\"\\",\\"hash\\":\\"\\"}</pre>"`
736+ ) ;
737+ } ) ;
738+
739+ it ( "does not encode characters in MemoryRouter (navigate)" , ( ) => {
740+ function Start ( ) {
741+ let navigate = useNavigate ( ) ;
742+ // eslint-disable-next-line react-hooks/exhaustive-deps
743+ React . useEffect ( ( ) => navigate ( "/with space" ) , [ ] ) ;
744+ return null ;
745+ }
746+ let ctx = render (
747+ < MemoryRouter >
748+ < Routes >
749+ < Route path = "/" element = { < Start /> } />
750+ < Route path = "/with space" element = { < ShowPath /> } />
751+ </ Routes >
752+ </ MemoryRouter >
753+ ) ;
754+
755+ expect ( ctx . container . innerHTML ) . toMatchInlineSnapshot (
756+ `"<pre>{\\"pathname\\":\\"/with space\\",\\"search\\":\\"\\",\\"hash\\":\\"\\"}</pre>"`
757+ ) ;
758+ } ) ;
759+
760+ it ( "does not encode characters in createMemoryRouter" , ( ) => {
761+ let router = createMemoryRouter (
762+ [ { path : "/with space" , element : < ShowPath /> } ] ,
763+ { initialEntries : [ "/with space" ] }
764+ ) ;
765+ let ctx = render ( < RouterProvider router = { router } /> ) ;
766+
767+ expect ( ctx . container . innerHTML ) . toMatchInlineSnapshot (
768+ `"<pre>{\\"pathname\\":\\"/with space\\",\\"search\\":\\"\\",\\"hash\\":\\"\\"}</pre>"`
769+ ) ;
770+ } ) ;
771+
772+ it ( "does not encode characters in createMemoryRouter (navigate)" , ( ) => {
773+ function Start ( ) {
774+ let navigate = useNavigate ( ) ;
775+ // eslint-disable-next-line react-hooks/exhaustive-deps
776+ React . useEffect ( ( ) => navigate ( "/with space" ) , [ ] ) ;
777+ return null ;
778+ }
779+ let router = createMemoryRouter ( [
780+ { path : "/" , element : < Start /> } ,
781+ { path : "/with space" , element : < ShowPath /> } ,
782+ ] ) ;
783+ let ctx = render ( < RouterProvider router = { router } /> ) ;
784+
785+ expect ( ctx . container . innerHTML ) . toMatchInlineSnapshot (
786+ `"<pre>{\\"pathname\\":\\"/with space\\",\\"search\\":\\"\\",\\"hash\\":\\"\\"}</pre>"`
787+ ) ;
788+ } ) ;
789+ } ) ;
790+
791+ describe ( "browser routers" , ( ) => {
792+ let testWindow : Window ;
793+
794+ beforeEach ( ( ) => {
795+ // Need to use our own custom DOM in order to get a working history
796+ const dom = new JSDOM ( `<!DOCTYPE html><html><body></body></html>` , {
797+ url : "https://remix.run/" ,
798+ } ) ;
799+ testWindow = dom . window as unknown as Window ;
800+ testWindow . history . pushState ( { } , "" , "/" ) ;
801+ } ) ;
802+
803+ it ( "encodes characters in BrowserRouter" , ( ) => {
804+ testWindow . history . replaceState ( null , "" , "/with space" ) ;
805+
806+ let ctx = render (
807+ < BrowserRouter window = { testWindow } >
808+ < Routes >
809+ < Route path = "/with space" element = { < ShowPath /> } />
810+ </ Routes >
811+ </ BrowserRouter >
812+ ) ;
813+
814+ expect ( testWindow . location . pathname ) . toBe ( "/with%20space" ) ;
815+ expect ( ctx . container . innerHTML ) . toMatchInlineSnapshot (
816+ `"<pre>{\\"pathname\\":\\"/with%20space\\",\\"search\\":\\"\\",\\"hash\\":\\"\\"}</pre>"`
817+ ) ;
818+ } ) ;
819+
820+ it ( "encodes characters in BrowserRouter (navigate)" , ( ) => {
821+ testWindow . history . replaceState ( null , "" , "/" ) ;
822+
823+ function Start ( ) {
824+ let navigate = useNavigate ( ) ;
825+ // eslint-disable-next-line react-hooks/exhaustive-deps
826+ React . useEffect ( ( ) => navigate ( "/with space" ) , [ ] ) ;
827+ return null ;
828+ }
829+
830+ let ctx = render (
831+ < BrowserRouter window = { testWindow } >
832+ < Routes >
833+ < Route path = "/" element = { < Start /> } />
834+ < Route path = "/with space" element = { < ShowPath /> } />
835+ </ Routes >
836+ </ BrowserRouter >
837+ ) ;
838+
839+ expect ( testWindow . location . pathname ) . toBe ( "/with%20space" ) ;
840+ expect ( ctx . container . innerHTML ) . toMatchInlineSnapshot (
841+ `"<pre>{\\"pathname\\":\\"/with%20space\\",\\"search\\":\\"\\",\\"hash\\":\\"\\"}</pre>"`
842+ ) ;
843+ } ) ;
844+
845+ it ( "encodes characters in createBrowserRouter" , ( ) => {
846+ testWindow . history . replaceState ( null , "" , "/with space" ) ;
847+
848+ let router = createBrowserRouter (
849+ [ { path : "/with space" , element : < ShowPath /> } ] ,
850+ { window : testWindow }
851+ ) ;
852+ let ctx = render ( < RouterProvider router = { router } /> ) ;
853+
854+ expect ( testWindow . location . pathname ) . toBe ( "/with%20space" ) ;
855+ expect ( ctx . container . innerHTML ) . toMatchInlineSnapshot (
856+ `"<pre>{\\"pathname\\":\\"/with%20space\\",\\"search\\":\\"\\",\\"hash\\":\\"\\"}</pre>"`
857+ ) ;
858+ } ) ;
859+
860+ it ( "encodes characters in createBrowserRouter (navigate)" , ( ) => {
861+ testWindow . history . replaceState ( null , "" , "/with space" ) ;
862+
863+ function Start ( ) {
864+ let navigate = useNavigate ( ) ;
865+ // eslint-disable-next-line react-hooks/exhaustive-deps
866+ React . useEffect ( ( ) => navigate ( "/with space" ) , [ ] ) ;
867+ return null ;
868+ }
869+
870+ let router = createBrowserRouter (
871+ [
872+ { path : "/" , element : < Start /> } ,
873+ { path : "/with space" , element : < ShowPath /> } ,
874+ ] ,
875+ { window : testWindow }
876+ ) ;
877+ let ctx = render ( < RouterProvider router = { router } /> ) ;
878+
879+ expect ( testWindow . location . pathname ) . toBe ( "/with%20space" ) ;
880+ expect ( ctx . container . innerHTML ) . toMatchInlineSnapshot (
881+ `"<pre>{\\"pathname\\":\\"/with%20space\\",\\"search\\":\\"\\",\\"hash\\":\\"\\"}</pre>"`
882+ ) ;
883+ } ) ;
884+ } ) ;
885+
886+ describe ( "hash routers" , ( ) => {
887+ let testWindow : Window ;
888+
889+ beforeEach ( ( ) => {
890+ // Need to use our own custom DOM in order to get a working history
891+ const dom = new JSDOM ( `<!DOCTYPE html><html><body></body></html>` , {
892+ url : "https://remix.run/" ,
893+ } ) ;
894+ testWindow = dom . window as unknown as Window ;
895+ testWindow . history . pushState ( { } , "" , "/" ) ;
896+ } ) ;
897+
898+ it ( "encodes characters in HashRouter" , ( ) => {
899+ testWindow . history . replaceState ( null , "" , "/#/with space" ) ;
900+
901+ let ctx = render (
902+ < HashRouter window = { testWindow } >
903+ < Routes >
904+ < Route path = "/with space" element = { < ShowPath /> } />
905+ </ Routes >
906+ </ HashRouter >
907+ ) ;
908+
909+ expect ( testWindow . location . pathname ) . toBe ( "/" ) ;
910+ expect ( testWindow . location . hash ) . toBe ( "#/with%20space" ) ;
911+ expect ( ctx . container . innerHTML ) . toMatchInlineSnapshot (
912+ `"<pre>{\\"pathname\\":\\"/with%20space\\",\\"search\\":\\"\\",\\"hash\\":\\"\\"}</pre>"`
913+ ) ;
914+ } ) ;
915+
916+ it ( "encodes characters in HashRouter (navigate)" , ( ) => {
917+ testWindow . history . replaceState ( null , "" , "/" ) ;
918+
919+ function Start ( ) {
920+ let navigate = useNavigate ( ) ;
921+ // eslint-disable-next-line react-hooks/exhaustive-deps
922+ React . useEffect ( ( ) => navigate ( "/with space" ) , [ ] ) ;
923+ return null ;
924+ }
925+
926+ let ctx = render (
927+ < HashRouter window = { testWindow } >
928+ < Routes >
929+ < Route path = "/" element = { < Start /> } />
930+ < Route path = "/with space" element = { < ShowPath /> } />
931+ </ Routes >
932+ </ HashRouter >
933+ ) ;
934+
935+ expect ( testWindow . location . pathname ) . toBe ( "/" ) ;
936+ expect ( testWindow . location . hash ) . toBe ( "#/with%20space" ) ;
937+ expect ( ctx . container . innerHTML ) . toMatchInlineSnapshot (
938+ `"<pre>{\\"pathname\\":\\"/with%20space\\",\\"search\\":\\"\\",\\"hash\\":\\"\\"}</pre>"`
939+ ) ;
940+ } ) ;
941+
942+ it ( "encodes characters in createHashRouter" , ( ) => {
943+ testWindow . history . replaceState ( null , "" , "/#/with space" ) ;
944+
945+ let router = createHashRouter (
946+ [ { path : "/with space" , element : < ShowPath /> } ] ,
947+ { window : testWindow }
948+ ) ;
949+ let ctx = render ( < RouterProvider router = { router } /> ) ;
950+
951+ expect ( testWindow . location . pathname ) . toBe ( "/" ) ;
952+ expect ( testWindow . location . hash ) . toBe ( "#/with%20space" ) ;
953+ expect ( ctx . container . innerHTML ) . toMatchInlineSnapshot (
954+ `"<pre>{\\"pathname\\":\\"/with%20space\\",\\"search\\":\\"\\",\\"hash\\":\\"\\"}</pre>"`
955+ ) ;
956+ } ) ;
957+
958+ it ( "encodes characters in createHashRouter (navigate)" , ( ) => {
959+ testWindow . history . replaceState ( null , "" , "/" ) ;
960+
961+ function Start ( ) {
962+ let navigate = useNavigate ( ) ;
963+ // eslint-disable-next-line react-hooks/exhaustive-deps
964+ React . useEffect ( ( ) => navigate ( "/with space" ) , [ ] ) ;
965+ return null ;
966+ }
967+
968+ let router = createHashRouter (
969+ [
970+ { path : "/" , element : < Start /> } ,
971+ { path : "/with space" , element : < ShowPath /> } ,
972+ ] ,
973+ { window : testWindow }
974+ ) ;
975+ let ctx = render ( < RouterProvider router = { router } /> ) ;
976+
977+ expect ( testWindow . location . pathname ) . toBe ( "/" ) ;
978+ expect ( testWindow . location . hash ) . toBe ( "#/with%20space" ) ;
979+ expect ( ctx . container . innerHTML ) . toMatchInlineSnapshot (
980+ `"<pre>{\\"pathname\\":\\"/with%20space\\",\\"search\\":\\"\\",\\"hash\\":\\"\\"}</pre>"`
981+ ) ;
982+ } ) ;
983+ } ) ;
984+ } ) ;
712985} ) ;
0 commit comments