import { render, screen } from "@testing-library/react"; import { resetFocusOnNavigation, useFocusOnNavigation, } from "./useFocusOnNavigation"; function Probe({ pathname }: { pathname: string }) { useFocusOnNavigation(pathname); return null; } beforeEach(() => { resetFocusOnNavigation(); document.body.innerHTML = ""; }); function mountMain() { const main = document.createElement("div"); main.id = "main-content"; main.tabIndex = -1; document.body.append(main); return main; } describe("useFocusOnNavigation", () => { it("does nothing on the first load, even with #main-content on the page", () => { const main = mountMain(); render(); expect(main).not.toHaveFocus(); }); it("focuses #main-content once per path change, never scrolling", () => { const main = mountMain(); const focus = jest.spyOn(main, "focus"); const { rerender } = render(); rerender(); expect(main).toHaveFocus(); expect(focus).toHaveBeenCalledTimes(1); expect(focus).toHaveBeenCalledWith({ preventScroll: true }); rerender(); expect(focus).toHaveBeenCalledTimes(1); }); describe("with a #hash in the URL (from /privacidade to /#contact)", () => { afterEach(() => { window.history.replaceState(null, "", "/"); }); function mountContact() { const contact = document.createElement("section"); contact.id = "contact"; contact.tabIndex = -1; document.body.append(contact); return contact; } it("leaves the focus to the anchor target when the new page has it", () => { const main = mountMain(); const contact = mountContact(); const focus = jest.spyOn(main, "focus"); const { rerender } = render(); window.history.replaceState(null, "", "/#contact"); contact.focus({ preventScroll: true }); rerender(); expect(focus).not.toHaveBeenCalled(); expect(contact).toHaveFocus(); }); it("still focuses #main-content when the hash points to nothing on the new page", () => { const main = mountMain(); mountContact(); const { rerender } = render(); window.history.replaceState(null, "", "/#nada"); rerender(); expect(main).toHaveFocus(); }); }); it("still tells a navigation from the first load when the component remounts (first segment changed)", () => { const main = mountMain(); const first = render(); first.unmount(); render(); expect(main).toHaveFocus(); }); it("survives a page without #main-content (a 404 shell, an error boundary)", () => { const { rerender } = render(); expect(() => rerender()).not.toThrow(); expect(screen.queryByRole("main")).toBeNull(); }); it("is not fooled by the double effect of React StrictMode", () => { const main = mountMain(); render(, { reactStrictMode: true }); expect(main).not.toHaveFocus(); }); });