Skip to content

Commit 9660acc

Browse files
authored
Use async singleton router for MemoryRouterProvider (#114)
2 parents 11c09a0 + d2e8fff commit 9660acc

File tree

4 files changed

+122
-3
lines changed

4 files changed

+122
-3
lines changed

.changeset/few-news-jam.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"next-router-mock": patch
3+
---
4+
5+
Enable MemoryRouterProvider to use the async singleton

package-lock.json

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
import React from "react";
2+
import { useRouter } from "next/router";
3+
import NextLink, { LinkProps } from "next/link";
4+
import { act, fireEvent, render, screen, waitFor } from "@testing-library/react";
5+
6+
import { MemoryRouterProvider } from "./index";
7+
import { default as singletonRouter } from "../async";
8+
9+
const TestLink = (linkProps: Partial<LinkProps>) => {
10+
const router = useRouter();
11+
return (
12+
<NextLink href="/test" {...linkProps}>
13+
Current route: "{router.asPath}"
14+
</NextLink>
15+
);
16+
};
17+
18+
describe("MemoryRouterProvider", () => {
19+
beforeEach(() => {
20+
singletonRouter.setCurrentUrl("/initial");
21+
});
22+
23+
it("should provide a router", () => {
24+
render(
25+
<MemoryRouterProvider async>
26+
<TestLink />
27+
</MemoryRouterProvider>
28+
);
29+
expect(screen.getByText(`Current route: "/initial"`)).toBeDefined();
30+
});
31+
32+
it("using the singleton router should update the URL", async () => {
33+
render(
34+
<MemoryRouterProvider async>
35+
<TestLink />
36+
</MemoryRouterProvider>
37+
);
38+
39+
// Navigate:
40+
expect(screen.getByText(`Current route: "/initial"`)).toBeDefined();
41+
await act(async () => await singletonRouter.push("/new-route"));
42+
await waitFor(() => expect(screen.getByText(`Current route: "/new-route"`)).toBeDefined());
43+
});
44+
45+
it("clicking a link should navigate to the new URL", async () => {
46+
render(
47+
<MemoryRouterProvider async>
48+
<TestLink />
49+
</MemoryRouterProvider>
50+
);
51+
expect(screen.getByText(`Current route: "/initial"`)).toBeDefined();
52+
fireEvent.click(screen.getByText(`Current route: "/initial"`));
53+
await waitFor(() => expect(screen.getByText(`Current route: "/test"`)).toBeDefined());
54+
});
55+
56+
describe("url", () => {
57+
it("an initial URL can be supplied", () => {
58+
render(
59+
<MemoryRouterProvider url="/example" async>
60+
<TestLink />
61+
</MemoryRouterProvider>
62+
);
63+
expect(screen.getByText(`Current route: "/example"`)).toBeDefined();
64+
});
65+
66+
it("an initial URL Object can be supplied", () => {
67+
render(
68+
<MemoryRouterProvider url={{ pathname: "/example", query: { foo: "bar" } }} async>
69+
<TestLink />
70+
</MemoryRouterProvider>
71+
);
72+
expect(screen.getByText(`Current route: "/example?foo=bar"`)).toBeDefined();
73+
});
74+
});
75+
76+
describe("events", () => {
77+
const eventHandlers = {
78+
onPush: jest.fn(),
79+
onReplace: jest.fn(),
80+
onRouteChangeStart: jest.fn(),
81+
onRouteChangeComplete: jest.fn(),
82+
};
83+
beforeEach(async () => {
84+
jest.clearAllMocks();
85+
});
86+
it("clicking a link should trigger the correct event handlers", async () => {
87+
render(
88+
<MemoryRouterProvider url="/initial" async {...eventHandlers}>
89+
<TestLink />
90+
</MemoryRouterProvider>
91+
);
92+
fireEvent.click(screen.getByText(`Current route: "/initial"`));
93+
await waitFor(() => expect(screen.getByText(`Current route: "/test"`)).not.toBeNull());
94+
expect(eventHandlers.onPush).toHaveBeenCalledWith("/test", { shallow: false });
95+
expect(eventHandlers.onReplace).not.toHaveBeenCalled();
96+
expect(eventHandlers.onRouteChangeStart).toHaveBeenCalledWith("/test", { shallow: false });
97+
expect(eventHandlers.onRouteChangeComplete).toHaveBeenCalledWith("/test", { shallow: false });
98+
});
99+
it("a 'replace' link triggers the correct handlers too", async () => {
100+
render(
101+
<MemoryRouterProvider url="/initial" async {...eventHandlers}>
102+
<TestLink replace />
103+
</MemoryRouterProvider>
104+
);
105+
fireEvent.click(screen.getByText(`Current route: "/initial"`));
106+
await waitFor(() => expect(screen.getByText(`Current route: "/test"`)).not.toBeNull());
107+
expect(eventHandlers.onPush).not.toHaveBeenCalledWith("/test", { shallow: false });
108+
expect(eventHandlers.onReplace).toHaveBeenCalled();
109+
expect(eventHandlers.onRouteChangeStart).toHaveBeenCalledWith("/test", { shallow: false });
110+
expect(eventHandlers.onRouteChangeComplete).toHaveBeenCalledWith("/test", { shallow: false });
111+
});
112+
});
113+
});

src/MemoryRouterProvider/MemoryRouterProvider.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import React, { FC, ReactNode, useMemo } from "react";
22

33
import { useMemoryRouter, MemoryRouter, Url, default as singletonRouter } from "../index";
4+
import { default as asyncSingletonRouter } from "../async";
45
import { MemoryRouterEventHandlers } from "../useMemoryRouter";
56
import { MemoryRouterContext } from "../MemoryRouterContext";
67

@@ -28,7 +29,7 @@ export function factory(dependencies: AbstractedNextDependencies) {
2829
return new MemoryRouter(url, async);
2930
}
3031
// Normally we'll just use the singleton:
31-
return singletonRouter;
32+
return async ? asyncSingletonRouter : singletonRouter;
3233
}, [url, async]);
3334

3435
const routerSnapshot = useMemoryRouter(memoryRouter, eventHandlers);

0 commit comments

Comments
 (0)