diff --git a/src/components/routing/nested-routes.test.tsx b/src/components/routing/nested-routes.test.tsx new file mode 100644 index 0000000..e9a5924 --- /dev/null +++ b/src/components/routing/nested-routes.test.tsx @@ -0,0 +1,113 @@ +import React from "react"; +import { NestedRoutes } from "./nested-routes"; +import { createMemoryHistory } from "history"; +import { render } from "@testing-library/react"; +import { Router } from "react-router-dom"; +import { RouteDefinition } from "../../interfaces/route-definition"; +import { Factory } from "rosie"; +import { FactoryType } from "../../tests/factories/factory-type"; +import faker from "faker"; + +describe("NestedRoutes", () => { + let nonExistentRoute: string; + const HomePage = () =>

Home

; + const NotFoundPage = () =>

404

; + + const homeRoute: RouteDefinition = Factory.build( + FactoryType.RouteDefinition.Default, + { path: "/", component: HomePage } + ); + + const nestedRoutes: RouteDefinition[] = Factory.buildList( + FactoryType.RouteDefinition.Nested, + 3 + ); + + const notFoundRoute = Factory.build( + FactoryType.RouteDefinition.Default, + { path: "/404", component: NotFoundPage } + ); + + let routes: RouteDefinition[] = []; + + beforeEach(() => { + nonExistentRoute = [ + "", // Preprends a / before the route + faker.random.alphaNumeric(5), + faker.random.alphaNumeric(5), + faker.random.alphaNumeric(5), + ].join("/"); + + routes = [...nestedRoutes, homeRoute, notFoundRoute]; + }); + + // ----------------------------------------------------------------------------------------- + // #region redirectToIfNotFound + // ----------------------------------------------------------------------------------------- + + describe("when pathname does not match any route path", () => { + describe("given redirectToIfNotFound has a value", () => { + test("it redirects to the redirectToIfNotFound route", () => { + // Arrange + const history = createMemoryHistory(); + // This should have no effect on whether or not the user is redirected for a non-existent route + const isAuthenticated = faker.random.boolean(); + const redirectToIfNotFound = notFoundRoute.path; // This is the important setup + + const App = () => ( + + + + ); + + // Act + history.push(nonExistentRoute); + const { getByRole } = render(); + + // Assert + expect(getByRole("heading")).toHaveTextContent("404"); + expect(history.location.pathname).toBe(redirectToIfNotFound); + }); + }); + + describe("given redirectToIfNotFound is null or empty", () => { + test("it does not redirect", () => { + // Arrange + const history = createMemoryHistory(); + // This should have no effect on whether or not the user is redirected for a non-existent route + const isAuthenticated = faker.random.boolean(); + // This is the important setup + const redirectToIfNotFound: + | string + | undefined = faker.random.arrayElement([ + undefined, + null, + "", + ]); + + const App = () => ( + + + + ); + + // Act + history.push(nonExistentRoute); + render(); + + // Assert + expect(history.location.pathname).toBe(nonExistentRoute); + }); + }); + }); + + // #endregion redirectToIfNotFound +}); diff --git a/src/components/routing/nested-routes.tsx b/src/components/routing/nested-routes.tsx index 6da32d5..e89157f 100644 --- a/src/components/routing/nested-routes.tsx +++ b/src/components/routing/nested-routes.tsx @@ -1,6 +1,6 @@ -import { CollectionUtils } from "andculturecode-javascript-core"; +import { CollectionUtils, StringUtils } from "andculturecode-javascript-core"; import { NestedRoute } from "./nested-route"; -import { Redirect } from "react-router-dom"; +import { Redirect, Switch } from "react-router-dom"; import React from "react"; import { RouteDefinition } from "../../interfaces/route-definition"; import { UnmatchedRoute } from "../../interfaces/unmatched-route"; @@ -27,12 +27,7 @@ interface NestedRoutesProps extends UnmatchedRoute, AuthenticatedRoute { const NestedRoutes: React.FC = ( props: NestedRoutesProps ) => { - const { - isAuthenticated, - redirectToIfNotFound, - redirectToIfUnauthenticated, - routes, - } = props; + const { redirectToIfNotFound, routes } = props; if (CollectionUtils.isEmpty(routes)) { return null; @@ -41,17 +36,14 @@ const NestedRoutes: React.FC = ( // TODO: Remove Fragment when issue fixed https://github.com/microsoft/TypeScript/issues/21699 return ( - {props.routes.map((route: RouteDefinition, i: number) => ( - - ))} - {redirectToIfNotFound != null && ( - - )} + + {routes.map((route: RouteDefinition, i: number) => ( + + ))} + {StringUtils.hasValue(redirectToIfNotFound) && ( + + )} + ); }; diff --git a/src/tests/factories/route-definition-factory.ts b/src/tests/factories/route-definition-factory.ts index 0fd7608..9391117 100644 --- a/src/tests/factories/route-definition-factory.ts +++ b/src/tests/factories/route-definition-factory.ts @@ -13,7 +13,7 @@ const RouteDefinitionFactory = Factory.define( .sequence("authRequired", () => false) .sequence("component", () => React.Fragment) .sequence("exact", () => true) - .sequence("path", (i: number) => `path${i}/`) + .sequence("path", (i: number) => `/path${i}`) .sequence("routes", () => {}); const RouteDefinitionNestedFactory = Factory.define( @@ -22,7 +22,7 @@ const RouteDefinitionNestedFactory = Factory.define( .sequence("authRequired", () => false) .sequence("component", () => React.Fragment) .sequence("exact", () => true) - .sequence("path", (i: number) => `path${i}/`) + .sequence("path", (i: number) => `/path${i}`) .sequence("routes", () => { return { nestedRoute: Factory.build(