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();
});
});