Skip to content

Commit 96d6c56

Browse files
test case: useBlocker facing unstable router object
1 parent c0f766f commit 96d6c56

File tree

1 file changed

+147
-0
lines changed

1 file changed

+147
-0
lines changed
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
import * as React from "react";
2+
import { render, screen, act } from "@testing-library/react";
3+
import userEvent from "@testing-library/user-event";
4+
import {
5+
Blocker,
6+
Link,
7+
Outlet,
8+
RouterProvider,
9+
createHashRouter,
10+
useBlocker,
11+
useLocation,
12+
} from "react-router";
13+
14+
const Confirm: React.FC<{ blocker: Blocker }> = ({ blocker }) => {
15+
const blocked = blocker.state === "blocked";
16+
return blocked ? (
17+
<div>
18+
<button onClick={() => blocker.reset()}>CANCEL</button>
19+
<button data-testid="bttnConfirm" onClick={() => blocker.proceed()}>
20+
CONFIRM
21+
</button>
22+
</div>
23+
) : null;
24+
};
25+
26+
describe("useBlocker", () => {
27+
it("is defensive against an unstable router object", async () => {
28+
const A: React.FC<{ foo: boolean }> = ({ foo }) => {
29+
return (
30+
<>
31+
<h2>A</h2>
32+
<Link to={"/"}>TO HOME</Link>
33+
<span data-testid="spanPageADisplayFoo">{`foo: ${foo}`}</span>
34+
</>
35+
);
36+
};
37+
38+
const B: React.FC<{
39+
setFoo: React.Dispatch<React.SetStateAction<boolean>>;
40+
}> = ({ setFoo }) => {
41+
let blocker = useBlocker(true);
42+
return (
43+
<>
44+
<h2>B</h2>
45+
<Link data-testid="linkBToHome" to={"/"}>
46+
TO HOME
47+
</Link>
48+
<span
49+
data-testid="spanReconstructRouter"
50+
style={{
51+
color: "blue",
52+
textDecoration: "underline",
53+
cursor: "pointer",
54+
}}
55+
onClick={() => setFoo((foo) => !foo)}
56+
>
57+
{"click to re-construct router"}
58+
</span>
59+
<Confirm blocker={blocker} />
60+
</>
61+
);
62+
};
63+
64+
const Root: React.FC = () => {
65+
const location = useLocation();
66+
return (
67+
<div
68+
style={{
69+
display: "flex",
70+
flexDirection: "column",
71+
alignItems: "center",
72+
}}
73+
>
74+
<h1>ROOT</h1>
75+
<div data-testid="location">{location.pathname}</div>
76+
<Outlet />
77+
</div>
78+
);
79+
};
80+
81+
const TestUnstableRouterObject: React.FC = () => {
82+
const [foo, setFoo] = React.useState<boolean>(false);
83+
const router = React.useMemo(() => {
84+
console.log("reconstructing router... foo is: ", foo);
85+
return createHashRouter([
86+
{
87+
path: "/",
88+
Component: Root,
89+
children: [
90+
{
91+
index: true,
92+
element: (
93+
<>
94+
<h1>HOME</h1>
95+
<Link data-testid="linkToPageA" to={"/a"}>
96+
TO PAGE A
97+
</Link>
98+
<Link data-testid="linkToPageB" to={"/b"}>
99+
TO PAGE B
100+
</Link>
101+
</>
102+
),
103+
},
104+
{
105+
path: "a",
106+
element: <A foo={foo} />,
107+
},
108+
{
109+
path: "b",
110+
element: <B setFoo={setFoo} />,
111+
},
112+
],
113+
},
114+
]);
115+
}, [foo]);
116+
117+
return <RouterProvider router={router} />;
118+
};
119+
120+
render(<TestUnstableRouterObject />);
121+
122+
let expectLocation = (location: string) =>
123+
expect(
124+
screen.getByTestId<HTMLDivElement>("location").textContent
125+
).toEqual(location);
126+
127+
expectLocation("/");
128+
129+
await userEvent.click(screen.getByTestId("linkToPageB"));
130+
expectLocation("/b");
131+
132+
await userEvent.click(
133+
screen.getByTestId<HTMLSpanElement>("spanReconstructRouter")
134+
);
135+
await userEvent.click(screen.getByTestId("linkBToHome"));
136+
137+
await userEvent.click(screen.getByTestId<HTMLButtonElement>("bttnConfirm"));
138+
expectLocation("/");
139+
140+
await userEvent.click(screen.getByTestId("linkToPageA"));
141+
expectLocation("/a");
142+
143+
expect(
144+
screen.getByTestId<HTMLSpanElement>("spanPageADisplayFoo").textContent
145+
).toEqual("foo: true");
146+
});
147+
});

0 commit comments

Comments
 (0)