import { fireEvent, render, screen } from "@testing-library/react"; import ContactOfferLink from "./ContactOfferLink"; import { CONTACT_KIND_EVENT } from "./contact-subject"; jest.mock("next/link", () => ({ __esModule: true, default: ({ children, prefetch, ...props }: { children?: React.ReactNode; prefetch?: boolean | null } & Record< string, unknown >) => ( {children} ), })); describe("ContactOfferLink", () => { it("links to the form with the type in the address, and prefetches nothing until hover or focus", () => { render( Começar pelo sistema , ); const link = screen.getByRole("link", { name: "Começar pelo sistema" }); expect(link).toHaveAttribute("href", "/?tipo=sistema#contact-form"); expect(link).toHaveClass("botao"); expect(link).toHaveAttribute("data-prefetch", "false"); fireEvent.mouseEnter(link); expect(link).toHaveAttribute("data-prefetch", "null"); }); it("tells the form which type was chosen, through a window event, when it is clicked", () => { const heard: unknown[] = []; const listener = (event: Event) => heard.push((event as CustomEvent).detail); window.addEventListener(CONTACT_KIND_EVENT, listener); try { render(Começar); fireEvent.click(screen.getByRole("link", { name: "Começar" })); expect(heard).toEqual(["revisao"]); } finally { window.removeEventListener(CONTACT_KIND_EVENT, listener); } }); it("still tells the form when another handler already cancelled the click (the home scroll manager does that to scroll by itself)", () => { const heard: unknown[] = []; const listener = (event: Event) => heard.push((event as CustomEvent).detail); const cancel = (event: Event) => event.preventDefault(); window.addEventListener(CONTACT_KIND_EVENT, listener); document.addEventListener("click", cancel, true); try { render(Começar); fireEvent.click(screen.getByRole("link", { name: "Começar" })); expect(heard).toEqual(["site"]); } finally { window.removeEventListener(CONTACT_KIND_EVENT, listener); document.removeEventListener("click", cancel, true); } }); }); describe("ContactOfferLink: rolagem até o formulário", () => { const scrollIntoView = jest.fn(); const original = window.matchMedia; let form: HTMLElement; const setReducedMotion = (reduced: boolean) => { window.matchMedia = jest.fn().mockImplementation((query: string) => ({ matches: reduced && query === "(prefers-reduced-motion: reduce)", media: query, addEventListener: jest.fn(), removeEventListener: jest.fn(), })); }; beforeEach(() => { scrollIntoView.mockReset(); HTMLElement.prototype.scrollIntoView = scrollIntoView; window.history.replaceState(null, "", "/"); setReducedMotion(false); form = document.createElement("section"); form.id = "contact-form"; document.body.append(form); }); afterEach(() => { form.remove(); window.matchMedia = original; window.history.replaceState(null, "", "/"); }); it("cancels the navigation on the home, rolls to #contact-form, focuses the form and leaves the address as /#contact-form", () => { const heard: unknown[] = []; const listener = (event: Event) => heard.push((event as CustomEvent).detail); window.addEventListener(CONTACT_KIND_EVENT, listener); const pushState = jest.spyOn(window.history, "pushState"); try { render( Começar pelo site, ); const link = screen.getByRole("link", { name: "Começar pelo site" }); // fireEvent devolve false quando alguém chamou preventDefault: o Link do Next não navega expect(fireEvent.click(link)).toBe(false); expect(heard).toEqual(["site"]); expect(scrollIntoView).toHaveBeenCalledTimes(1); expect(scrollIntoView).toHaveBeenCalledWith({ behavior: "smooth", block: "start", }); expect(form).toHaveFocus(); expect(window.location.pathname + window.location.search).toBe("/"); expect(window.location.hash).toBe("#contact-form"); expect(pushState).not.toHaveBeenCalled(); } finally { pushState.mockRestore(); window.removeEventListener(CONTACT_KIND_EVENT, listener); } }); it("jumps without animation when the visitor asks for reduced motion", () => { setReducedMotion(true); render(Começar); fireEvent.click(screen.getByRole("link", { name: "Começar" })); expect(scrollIntoView).toHaveBeenCalledWith({ behavior: "instant", block: "start", }); }); it("drops the ?tipo= left by an earlier offer or by the arrival from another page, and keeps the other parameters", () => { window.history.replaceState( null, "", "/?utm_source=ads&tipo=site#contact-form", ); render(Começar); fireEvent.click(screen.getByRole("link", { name: "Começar" })); expect(window.location.search).toBe("?utm_source=ads"); expect(window.location.hash).toBe("#contact-form"); }); it("leaves the click alone when the scroll manager already took it (no second scroll), still cleaning the address", () => { window.history.replaceState(null, "", "/?tipo=site#contact-form"); const cancel = (event: Event) => event.preventDefault(); document.addEventListener("click", cancel, true); try { render(Começar); fireEvent.click(screen.getByRole("link", { name: "Começar" })); expect(scrollIntoView).not.toHaveBeenCalled(); expect(window.location.search).toBe(""); expect(window.location.hash).toBe("#contact-form"); } finally { document.removeEventListener("click", cancel, true); } }); it("does not take over a click with a modifier key (new tab) or the middle button", () => { render(Começar); const link = screen.getByRole("link", { name: "Começar" }); expect(fireEvent.click(link, { ctrlKey: true })).toBe(true); expect(fireEvent.click(link, { metaKey: true })).toBe(true); expect(fireEvent.click(link, { shiftKey: true })).toBe(true); expect(fireEvent.click(link, { button: 1 })).toBe(true); expect(scrollIntoView).not.toHaveBeenCalled(); expect(window.location.hash).toBe(""); }); it("does not cancel anything outside the home: the Link navigates to /?tipo=…#contact-form", () => { window.history.replaceState(null, "", "/privacidade"); render(Começar); const link = screen.getByRole("link", { name: "Começar" }); expect(link).toHaveAttribute("href", "/?tipo=revisao#contact-form"); expect(fireEvent.click(link)).toBe(true); expect(scrollIntoView).not.toHaveBeenCalled(); expect(window.location.pathname).toBe("/privacidade"); expect(window.location.hash).toBe(""); }); it("falls back to the Link navigation when the page has no #contact-form", () => { form.remove(); render(Começar); expect(fireEvent.click(screen.getByRole("link", { name: "Começar" }))).toBe( true, ); expect(scrollIntoView).not.toHaveBeenCalled(); document.body.append(form); }); });