-
-
Notifications
You must be signed in to change notification settings - Fork 10.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix(react-router-dom): Fix
usePrompt
invalid blocker state transiti…
…on (#10687) Co-authored-by: Matt Brophy <matt@brophy.org>
- Loading branch information
1 parent
1a68ac1
commit 1b8ee16
Showing
4 changed files
with
139 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"react-router-dom": patch | ||
--- | ||
|
||
Reorder effects in `unstable_usePrompt` to avoid throwing an exception if the prompt is unblocked and a navigation is performed syncronously |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -223,3 +223,4 @@ | |
- yuleicul | ||
- zheng-chuang | ||
- istarkov | ||
- louis-young |
126 changes: 126 additions & 0 deletions
126
packages/react-router-dom/__tests__/use-prompt-test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
import * as React from "react"; | ||
import { fireEvent, render, screen, waitFor } from "@testing-library/react"; | ||
import { | ||
Link, | ||
RouterProvider, | ||
createBrowserRouter, | ||
unstable_usePrompt as usePrompt, | ||
} from "../index"; | ||
import "@testing-library/jest-dom"; | ||
import { JSDOM } from "jsdom"; | ||
|
||
describe("usePrompt", () => { | ||
afterEach(() => { | ||
jest.clearAllMocks(); | ||
}); | ||
|
||
describe("when navigation is blocked", () => { | ||
it("shows window.confirm and blocks navigation when it returns false", async () => { | ||
let testWindow = getWindowImpl("/"); | ||
const windowConfirmMock = jest | ||
.spyOn(window, "confirm") | ||
.mockImplementationOnce(() => false); | ||
|
||
let router = createBrowserRouter( | ||
[ | ||
{ | ||
path: "/", | ||
Component() { | ||
usePrompt({ when: true, message: "Are you sure??" }); | ||
return <Link to="/arbitrary">Navigate</Link>; | ||
}, | ||
}, | ||
{ | ||
path: "/arbitrary", | ||
Component: () => <h1>Arbitrary</h1>, | ||
}, | ||
], | ||
{ window: testWindow } | ||
); | ||
|
||
render(<RouterProvider router={router} />); | ||
expect(screen.getByText("Navigate")).toBeInTheDocument(); | ||
|
||
fireEvent.click(screen.getByText("Navigate")); | ||
await new Promise((r) => setTimeout(r, 0)); | ||
|
||
expect(windowConfirmMock).toHaveBeenNthCalledWith(1, "Are you sure??"); | ||
expect(screen.getByText("Navigate")).toBeInTheDocument(); | ||
}); | ||
|
||
it("shows window.confirm and navigates when it returns true", async () => { | ||
let testWindow = getWindowImpl("/"); | ||
const windowConfirmMock = jest | ||
.spyOn(window, "confirm") | ||
.mockImplementationOnce(() => true); | ||
|
||
let router = createBrowserRouter( | ||
[ | ||
{ | ||
path: "/", | ||
Component() { | ||
usePrompt({ when: true, message: "Are you sure??" }); | ||
return <Link to="/arbitrary">Navigate</Link>; | ||
}, | ||
}, | ||
{ | ||
path: "/arbitrary", | ||
Component: () => <h1>Arbitrary</h1>, | ||
}, | ||
], | ||
{ window: testWindow } | ||
); | ||
|
||
render(<RouterProvider router={router} />); | ||
expect(screen.getByText("Navigate")).toBeInTheDocument(); | ||
|
||
fireEvent.click(screen.getByText("Navigate")); | ||
await waitFor(() => screen.getByText("Arbitrary")); | ||
|
||
expect(windowConfirmMock).toHaveBeenNthCalledWith(1, "Are you sure??"); | ||
expect(screen.getByText("Arbitrary")).toBeInTheDocument(); | ||
}); | ||
}); | ||
|
||
describe("when navigation is not blocked", () => { | ||
it("navigates without showing window.confirm", async () => { | ||
let testWindow = getWindowImpl("/"); | ||
const windowConfirmMock = jest | ||
.spyOn(window, "confirm") | ||
.mockImplementation(() => true); | ||
|
||
let router = createBrowserRouter( | ||
[ | ||
{ | ||
path: "/", | ||
Component() { | ||
usePrompt({ when: false, message: "Are you sure??" }); | ||
return <Link to="/arbitrary">Navigate</Link>; | ||
}, | ||
}, | ||
{ | ||
path: "/arbitrary", | ||
Component: () => <h1>Arbitrary</h1>, | ||
}, | ||
], | ||
{ window: testWindow } | ||
); | ||
|
||
render(<RouterProvider router={router} />); | ||
expect(screen.getByText("Navigate")).toBeInTheDocument(); | ||
|
||
fireEvent.click(screen.getByText("Navigate")); | ||
await waitFor(() => screen.getByText("Arbitrary")); | ||
|
||
expect(windowConfirmMock).not.toHaveBeenCalled(); | ||
expect(screen.getByText("Arbitrary")).toBeInTheDocument(); | ||
}); | ||
}); | ||
}); | ||
|
||
function getWindowImpl(initialUrl: string, isHash = false): Window { | ||
// Need to use our own custom DOM in order to get a working history | ||
const dom = new JSDOM(`<!DOCTYPE html>`, { url: "http://localhost/" }); | ||
dom.window.history.replaceState(null, "", (isHash ? "#" : "") + initialUrl); | ||
return dom.window as unknown as Window; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters