Skip to content

Commit

Permalink
Use async singleton router for MemoryRouterProvider (#114)
Browse files Browse the repository at this point in the history
  • Loading branch information
scottrippey committed Feb 5, 2024
2 parents 11c09a0 + d2e8fff commit 9660acc
Show file tree
Hide file tree
Showing 4 changed files with 122 additions and 3 deletions.
5 changes: 5 additions & 0 deletions .changeset/few-news-jam.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"next-router-mock": patch
---

Enable MemoryRouterProvider to use the async singleton
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

113 changes: 113 additions & 0 deletions src/MemoryRouterProvider/MemoryRouterProvider.async.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import React from "react";
import { useRouter } from "next/router";
import NextLink, { LinkProps } from "next/link";
import { act, fireEvent, render, screen, waitFor } from "@testing-library/react";

import { MemoryRouterProvider } from "./index";
import { default as singletonRouter } from "../async";

const TestLink = (linkProps: Partial<LinkProps>) => {
const router = useRouter();
return (
<NextLink href="/test" {...linkProps}>
Current route: "{router.asPath}"
</NextLink>
);
};

describe("MemoryRouterProvider", () => {
beforeEach(() => {
singletonRouter.setCurrentUrl("/initial");
});

it("should provide a router", () => {
render(
<MemoryRouterProvider async>
<TestLink />
</MemoryRouterProvider>
);
expect(screen.getByText(`Current route: "/initial"`)).toBeDefined();
});

it("using the singleton router should update the URL", async () => {
render(
<MemoryRouterProvider async>
<TestLink />
</MemoryRouterProvider>
);

// Navigate:
expect(screen.getByText(`Current route: "/initial"`)).toBeDefined();
await act(async () => await singletonRouter.push("/new-route"));
await waitFor(() => expect(screen.getByText(`Current route: "/new-route"`)).toBeDefined());
});

it("clicking a link should navigate to the new URL", async () => {
render(
<MemoryRouterProvider async>
<TestLink />
</MemoryRouterProvider>
);
expect(screen.getByText(`Current route: "/initial"`)).toBeDefined();
fireEvent.click(screen.getByText(`Current route: "/initial"`));
await waitFor(() => expect(screen.getByText(`Current route: "/test"`)).toBeDefined());
});

describe("url", () => {
it("an initial URL can be supplied", () => {
render(
<MemoryRouterProvider url="/example" async>
<TestLink />
</MemoryRouterProvider>
);
expect(screen.getByText(`Current route: "/example"`)).toBeDefined();
});

it("an initial URL Object can be supplied", () => {
render(
<MemoryRouterProvider url={{ pathname: "/example", query: { foo: "bar" } }} async>
<TestLink />
</MemoryRouterProvider>
);
expect(screen.getByText(`Current route: "/example?foo=bar"`)).toBeDefined();
});
});

describe("events", () => {
const eventHandlers = {
onPush: jest.fn(),
onReplace: jest.fn(),
onRouteChangeStart: jest.fn(),
onRouteChangeComplete: jest.fn(),
};
beforeEach(async () => {
jest.clearAllMocks();
});
it("clicking a link should trigger the correct event handlers", async () => {
render(
<MemoryRouterProvider url="/initial" async {...eventHandlers}>
<TestLink />
</MemoryRouterProvider>
);
fireEvent.click(screen.getByText(`Current route: "/initial"`));
await waitFor(() => expect(screen.getByText(`Current route: "/test"`)).not.toBeNull());
expect(eventHandlers.onPush).toHaveBeenCalledWith("/test", { shallow: false });
expect(eventHandlers.onReplace).not.toHaveBeenCalled();
expect(eventHandlers.onRouteChangeStart).toHaveBeenCalledWith("/test", { shallow: false });
expect(eventHandlers.onRouteChangeComplete).toHaveBeenCalledWith("/test", { shallow: false });
});
it("a 'replace' link triggers the correct handlers too", async () => {
render(
<MemoryRouterProvider url="/initial" async {...eventHandlers}>
<TestLink replace />
</MemoryRouterProvider>
);
fireEvent.click(screen.getByText(`Current route: "/initial"`));
await waitFor(() => expect(screen.getByText(`Current route: "/test"`)).not.toBeNull());
expect(eventHandlers.onPush).not.toHaveBeenCalledWith("/test", { shallow: false });
expect(eventHandlers.onReplace).toHaveBeenCalled();
expect(eventHandlers.onRouteChangeStart).toHaveBeenCalledWith("/test", { shallow: false });
expect(eventHandlers.onRouteChangeComplete).toHaveBeenCalledWith("/test", { shallow: false });
});
});
});
3 changes: 2 additions & 1 deletion src/MemoryRouterProvider/MemoryRouterProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React, { FC, ReactNode, useMemo } from "react";

import { useMemoryRouter, MemoryRouter, Url, default as singletonRouter } from "../index";
import { default as asyncSingletonRouter } from "../async";
import { MemoryRouterEventHandlers } from "../useMemoryRouter";
import { MemoryRouterContext } from "../MemoryRouterContext";

Expand Down Expand Up @@ -28,7 +29,7 @@ export function factory(dependencies: AbstractedNextDependencies) {
return new MemoryRouter(url, async);
}
// Normally we'll just use the singleton:
return singletonRouter;
return async ? asyncSingletonRouter : singletonRouter;
}, [url, async]);

const routerSnapshot = useMemoryRouter(memoryRouter, eventHandlers);
Expand Down

0 comments on commit 9660acc

Please sign in to comment.