Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 113 additions & 0 deletions src/components/routing/nested-routes.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import React from "react";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@brandongregoryscott Thank you for writing tests for this!

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 = () => <h1>Home</h1>;
const NotFoundPage = () => <h1>404</h1>;

const homeRoute: RouteDefinition = Factory.build<RouteDefinition>(
FactoryType.RouteDefinition.Default,
{ path: "/", component: HomePage }
);

const nestedRoutes: RouteDefinition[] = Factory.buildList(
FactoryType.RouteDefinition.Nested,
3
);

const notFoundRoute = Factory.build<RouteDefinition>(
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 = () => (
<Router history={history}>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thought: Could be wrong, but think you could spare the extra setup of 'history' by using MemoryRouter.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, yeah, I was seeing that in the docs too. Wasn't sure how I would simulate a navigation change, though it looks like MemoryRouter has an initialEntries prop which may have worked the same way.

<NestedRoutes
isAuthenticated={isAuthenticated}
redirectToIfNotFound={redirectToIfNotFound}
routes={routes}
/>
</Router>
);

// Act
history.push(nonExistentRoute);
const { getByRole } = render(<App />);

// 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 = () => (
<Router history={history}>
<NestedRoutes
isAuthenticated={isAuthenticated}
redirectToIfNotFound={redirectToIfNotFound}
routes={routes}
/>
</Router>
);

// Act
history.push(nonExistentRoute);
render(<App />);

// Assert
expect(history.location.pathname).toBe(nonExistentRoute);
});
});
});

// #endregion redirectToIfNotFound
});
30 changes: 11 additions & 19 deletions src/components/routing/nested-routes.tsx
Original file line number Diff line number Diff line change
@@ -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";
Expand All @@ -27,12 +27,7 @@ interface NestedRoutesProps extends UnmatchedRoute, AuthenticatedRoute {
const NestedRoutes: React.FC<NestedRoutesProps> = (
props: NestedRoutesProps
) => {
const {
isAuthenticated,
redirectToIfNotFound,
redirectToIfUnauthenticated,
routes,
} = props;
const { redirectToIfNotFound, routes } = props;

if (CollectionUtils.isEmpty(routes)) {
return null;
Expand All @@ -41,17 +36,14 @@ const NestedRoutes: React.FC<NestedRoutesProps> = (
// TODO: Remove Fragment when issue fixed https://github.com/microsoft/TypeScript/issues/21699
return (
<React.Fragment>
{props.routes.map((route: RouteDefinition, i: number) => (
<NestedRoute
isAuthenticated={isAuthenticated}
key={i}
redirectToIfUnauthenticated={redirectToIfUnauthenticated}
route={route}
/>
))}
{redirectToIfNotFound != null && (
<Redirect to={redirectToIfNotFound} />
)}
<Switch>
{routes.map((route: RouteDefinition, i: number) => (
<NestedRoute {...props} {...route} key={i} route={route} />
))}
{StringUtils.hasValue(redirectToIfNotFound) && (
<Redirect to={redirectToIfNotFound!} />
)}
</Switch>
</React.Fragment>
);
};
Expand Down
4 changes: 2 additions & 2 deletions src/tests/factories/route-definition-factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const RouteDefinitionFactory = Factory.define<RouteDefinition>(
.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<RouteDefinition>(
Expand All @@ -22,7 +22,7 @@ const RouteDefinitionNestedFactory = Factory.define<RouteDefinition>(
.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<RouteDefinition>(
Expand Down