O botão das ofertas rola até o contato com o tipo marcado e não quebra mais as âncoras; ofertas com o que você recebe e primeiro passo, logo depois da vitrine; bloco de IA fora da home; cartão largo da SEV no hero do celular; separador da faixa de clientes só entre itens; favicon de 13 para 1,8 KB; pré-carregamento só no hover e nunca de /#... fora da home; X do menu e cabeçalho opaco; WhatsApp com "o case"; /ia com 3 passos e modelo de relatório ilustrativo; Sobre alinhado ao currículo; CSP Report-Only com os domínios do Ads.
202 lines
7.5 KiB
TypeScript
202 lines
7.5 KiB
TypeScript
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
|
|
>) => (
|
|
<a data-prefetch={String(prefetch)} {...props}>
|
|
{children}
|
|
</a>
|
|
),
|
|
}));
|
|
|
|
describe("ContactOfferLink", () => {
|
|
it("links to the form with the type in the address, and prefetches nothing until hover or focus", () => {
|
|
render(
|
|
<ContactOfferLink kind="sistema" className="botao">
|
|
Começar pelo sistema
|
|
</ContactOfferLink>,
|
|
);
|
|
|
|
const link = screen.getByRole("link", { name: "Começar pelo sistema" });
|
|
expect(link).toHaveAttribute("href", "/?tipo=sistema#contact");
|
|
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(<ContactOfferLink kind="revisao">Começar</ContactOfferLink>);
|
|
|
|
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(<ContactOfferLink kind="site">Começar</ContactOfferLink>);
|
|
|
|
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";
|
|
document.body.append(form);
|
|
});
|
|
|
|
afterEach(() => {
|
|
form.remove();
|
|
window.matchMedia = original;
|
|
window.history.replaceState(null, "", "/");
|
|
});
|
|
|
|
it("cancels the navigation on the home, rolls to #contact, focuses the form and leaves the address as /#contact", () => {
|
|
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(
|
|
<ContactOfferLink kind="site">Começar pelo site</ContactOfferLink>,
|
|
);
|
|
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");
|
|
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(<ContactOfferLink kind="sistema">Começar</ContactOfferLink>);
|
|
|
|
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");
|
|
render(<ContactOfferLink kind="revisao">Começar</ContactOfferLink>);
|
|
|
|
fireEvent.click(screen.getByRole("link", { name: "Começar" }));
|
|
expect(window.location.search).toBe("?utm_source=ads");
|
|
expect(window.location.hash).toBe("#contact");
|
|
});
|
|
|
|
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");
|
|
const cancel = (event: Event) => event.preventDefault();
|
|
document.addEventListener("click", cancel, true);
|
|
try {
|
|
render(<ContactOfferLink kind="sistema">Começar</ContactOfferLink>);
|
|
|
|
fireEvent.click(screen.getByRole("link", { name: "Começar" }));
|
|
expect(scrollIntoView).not.toHaveBeenCalled();
|
|
expect(window.location.search).toBe("");
|
|
expect(window.location.hash).toBe("#contact");
|
|
} finally {
|
|
document.removeEventListener("click", cancel, true);
|
|
}
|
|
});
|
|
|
|
it("does not take over a click with a modifier key (new tab) or the middle button", () => {
|
|
render(<ContactOfferLink kind="site">Começar</ContactOfferLink>);
|
|
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", () => {
|
|
window.history.replaceState(null, "", "/privacidade");
|
|
render(<ContactOfferLink kind="revisao">Começar</ContactOfferLink>);
|
|
const link = screen.getByRole("link", { name: "Começar" });
|
|
|
|
expect(link).toHaveAttribute("href", "/?tipo=revisao#contact");
|
|
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.remove();
|
|
render(<ContactOfferLink kind="site">Começar</ContactOfferLink>);
|
|
|
|
expect(fireEvent.click(screen.getByRole("link", { name: "Começar" }))).toBe(
|
|
true,
|
|
);
|
|
expect(scrollIntoView).not.toHaveBeenCalled();
|
|
document.body.append(form);
|
|
});
|
|
});
|