Files
portifolio-evandrodevbr/app/components/FloatingWhatsApp.route.test.tsx
T

138 lines
5.1 KiB
TypeScript

import { act, render, screen } from "@testing-library/react";
import { usePathname } from "next/navigation";
import type { WhatsAppContexts } from "@/lib/whatsapp";
import FloatingWhatsApp from "./FloatingWhatsApp";
/**
* O botão vive em app/layout.tsx e vale em todo o site: a mensagem acompanha a página aberta, e a observação
* do hero e do contato (que só existem na home) recomeça a cada troca de página.
*/
const contexts: WhatsAppContexts = {
// o whatsappSubject do serviço (lib/site-content.ts): uma frase em minúsculas, que cabe em "Quero conversar sobre {assunto}."
services: { "criacao-de-sites": "um site que traz pedidos pelo WhatsApp" },
cases: { "sev-modaintima": "SEV Moda Íntima" },
};
type Entry = Pick<IntersectionObserverEntry, "target" | "isIntersecting">;
const instances: FakeObserver[] = [];
const originalObserver = global.IntersectionObserver;
class FakeObserver {
disconnect = jest.fn();
constructor(private callback: IntersectionObserverCallback) {
instances.push(this);
}
observe = jest.fn();
unobserve = jest.fn();
takeRecords = () => [];
report(entries: Entry[]) {
act(() =>
this.callback(entries as IntersectionObserverEntry[], this as never),
);
}
}
const floatingLink = () =>
screen.queryByRole("link", { name: "Conversar no WhatsApp" });
const messageOf = (link: HTMLElement | null) =>
new URL(link?.getAttribute("href") ?? "").searchParams.get("text");
beforeEach(() => {
instances.length = 0;
global.IntersectionObserver =
FakeObserver as unknown as typeof IntersectionObserver;
});
afterEach(() => {
global.IntersectionObserver = originalObserver;
document.getElementById("hero-contact-cta")?.remove();
document.getElementById("contact")?.remove();
});
describe("message by route", () => {
it.each([
[
"/servicos/criacao-de-sites",
"Quero conversar sobre um site que traz pedidos pelo WhatsApp.",
],
[
"/cases/sev-modaintima",
"Quero conversar sobre um projeto parecido com o case SEV Moda Íntima.",
],
["/ia", "Quero conversar sobre a revisão de um aplicativo feito com IA."],
// a mesma frase do botão do hero e do cabeçalho de cada página
["/", "Quero conversar sobre um site ou sistema para a minha empresa."],
["/servicos", "Quero conversar sobre sites, sistemas e aplicativos."],
[
"/cases",
"Quero conversar sobre um projeto parecido com os cases do portfólio.",
],
[
"/curriculo",
"Quero conversar sobre um projeto, depois de ver o seu currículo.",
],
["/privacidade", "Quero conversar sobre um projeto."],
["/servicos/inexistente", "Quero conversar sobre um projeto."],
])("on %s", (pathname, sentence) => {
jest.mocked(usePathname).mockReturnValue(pathname);
// sem hero nem contato na página, o botão aparece de imediato
render(<FloatingWhatsApp contexts={contexts} />);
expect(messageOf(floatingLink())).toContain(sentence);
});
it("falls back to a generic message without contexts", () => {
jest.mocked(usePathname).mockReturnValue("/servicos/criacao-de-sites");
render(<FloatingWhatsApp />);
expect(messageOf(floatingLink())).toContain(
"Quero conversar sobre um projeto.",
);
});
});
describe("page changes", () => {
it("observes the hero and the contact again on every page, hiding the button until the new report", () => {
jest.mocked(usePathname).mockReturnValue("/cases/sev-modaintima");
const { rerender } = render(<FloatingWhatsApp contexts={contexts} />);
// páginas de case não têm hero nem contato: o botão está à vista
expect(floatingLink()).toBeInTheDocument();
expect(instances).toHaveLength(0);
// volta à home, que tem os dois convites
document.body.insertAdjacentHTML(
"beforeend",
'<a id="hero-contact-cta" href="/#contact">Conversar</a><section id="contact"></section>',
);
jest.mocked(usePathname).mockReturnValue("/");
rerender(<FloatingWhatsApp contexts={contexts} />);
// sem piscar: some até o observador dizer que o hero saiu da tela
expect(floatingLink()).toBeNull();
expect(instances).toHaveLength(1);
const hero = document.getElementById("hero-contact-cta") as Element;
const contact = document.getElementById("contact") as Element;
instances[0].report([
{ target: hero, isIntersecting: false },
{ target: contact, isIntersecting: false },
]);
expect(floatingLink()).toBeInTheDocument();
expect(messageOf(floatingLink())).toContain(
"Quero conversar sobre um site ou sistema para a minha empresa.",
);
// sai da home para um serviço: o observador antigo é desligado e o botão passa a falar do serviço
document.getElementById("hero-contact-cta")?.remove();
document.getElementById("contact")?.remove();
jest.mocked(usePathname).mockReturnValue("/servicos/criacao-de-sites");
rerender(<FloatingWhatsApp contexts={contexts} />);
expect(instances[0].disconnect).toHaveBeenCalled();
expect(messageOf(floatingLink())).toContain(
"Quero conversar sobre um site que traz pedidos pelo WhatsApp.",
);
});
});