Files
portifolio-evandrodevbr/app/components/FloatingWhatsApp.test.tsx
T
Evandro Fonseca Junior f8545cd442 feat(home): ofertas que vendem o título, WhatsApp no hero e menu do celular acessível
Três ofertas (site que traz pedidos pelo WhatsApp, sistema para tirar a operação da planilha, revisão de sistema existente) com miniatura e formulário com o tipo escolhido; botão principal do hero abre o WhatsApp; moldura da SEV no hero do celular; menu fecha ao navegar, Esc, foco preso incluindo a barra de cookies e espaço para ela; CinematicHome como componente de servidor com ilha cliente; /ia com H1 de oferta e cartão do que você recebe; botão flutuante em aside com transform; AVIF antes de WebP; copy sem repetir contato direto.
2026-10-14 17:01:36 -03:00

160 lines
4.8 KiB
TypeScript

import { act, fireEvent, render, screen } from "@testing-library/react";
import FloatingWhatsApp from "./FloatingWhatsApp";
const CONSENT_KEY = "evandro.analytics-consent.v1";
type Entry = Pick<IntersectionObserverEntry, "target" | "isIntersecting">;
let notify: (entries: Entry[]) => void = () => {};
const originalObserver = global.IntersectionObserver;
class FakeObserver {
constructor(callback: IntersectionObserverCallback) {
notify = (entries) =>
callback(entries as IntersectionObserverEntry[], this as never);
}
observe = jest.fn();
unobserve = jest.fn();
disconnect = jest.fn();
takeRecords = () => [];
}
function Page({ withInvites = true }: { withInvites?: boolean }) {
return (
<>
{withInvites && (
<>
<a id="hero-contact-cta" href="/#contact">
Conversar sobre meu projeto
</a>
<section id="contact" aria-label="Contato" />
</>
)}
<FloatingWhatsApp />
</>
);
}
function setVisibility(hero: boolean, contact: boolean) {
act(() =>
notify([
{
target: document.getElementById("hero-contact-cta") as Element,
isIntersecting: hero,
},
{
target: document.getElementById("contact") as Element,
isIntersecting: contact,
},
]),
);
}
const floatingLink = () =>
screen.queryByRole("link", { name: "Conversar no WhatsApp" });
beforeEach(() => {
global.IntersectionObserver =
FakeObserver as unknown as typeof IntersectionObserver;
jest.replaceProperty(process.env, "NODE_ENV", "production");
localStorage.setItem(CONSENT_KEY, "granted");
window.gtag = jest.fn();
});
afterEach(() => {
global.IntersectionObserver = originalObserver;
jest.restoreAllMocks();
localStorage.clear();
delete window.gtag;
});
it("stays hidden until the observer reports, then shows a 56px WhatsApp shortcut for small screens", () => {
render(<Page />);
expect(floatingLink()).toBeNull();
setVisibility(false, false);
const link = floatingLink();
expect(link).toBeInTheDocument();
expect(link).toHaveAttribute("target", "_blank");
expect(link).toHaveAttribute("rel", "noopener noreferrer");
const url = new URL(link?.getAttribute("href") ?? "");
expect(url.origin).toBe("https://wa.me");
expect(url.searchParams.get("text")).toContain(
"Quero conversar sobre um projeto.",
);
// alvo de 56 px, canto inferior direito, só abaixo de 768 px
expect(link).toHaveClass("h-14", "w-14");
expect(link?.parentElement).toHaveClass("fixed", "right-sp-4", "md:hidden");
});
it("hides while the hero button is on screen", () => {
render(<Page />);
setVisibility(true, false);
expect(floatingLink()).toBeNull();
setVisibility(false, false);
expect(floatingLink()).toBeInTheDocument();
});
it("hides while the contact section is on screen", () => {
render(<Page />);
setVisibility(false, true);
expect(floatingLink()).toBeNull();
setVisibility(false, false);
expect(floatingLink()).toBeInTheDocument();
});
it("shows right away on a page that has neither the hero button nor the contact section", () => {
render(<Page withInvites={false} />);
expect(floatingLink()).toBeInTheDocument();
});
it("lifts itself by the height of the open cookie bar", () => {
render(<Page />);
setVisibility(false, false);
expect(floatingLink()?.parentElement?.className).toContain(
"var(--cookie-bar-height,0px)",
);
});
it("lifts with a transform, not by animating `bottom`, so the layout does not shift", () => {
render(<Page />);
setVisibility(false, false);
const className = floatingLink()?.parentElement?.className ?? "";
expect(className).toContain("-translate-y-[var(--cookie-bar-height,0px)]");
expect(className).toContain("transition-transform");
expect(className).toContain("motion-reduce:transition-none");
// o `bottom` fica fixo (margem e área segura) e a barra de cookies não entra nele
expect(className).not.toContain("transition-[bottom]");
expect(className).toContain(
"bottom-[calc(var(--sp-4)+env(safe-area-inset-bottom))]",
);
});
it("sits in a named complementary region (the 'region' landmark rule), which only exists while the shortcut shows", () => {
render(<Page />);
expect(screen.queryByRole("complementary")).toBeNull();
setVisibility(false, false);
const region = screen.getByRole("complementary", { name: "Contato rápido" });
expect(region.tagName).toBe("ASIDE");
expect(region).toContainElement(floatingLink());
});
it("reports the click as a floating WhatsApp handoff", () => {
render(<Page />);
setVisibility(false, false);
fireEvent.click(floatingLink() as HTMLElement, { button: 0 });
expect(window.gtag).toHaveBeenCalledTimes(1);
expect(window.gtag).toHaveBeenCalledWith(
"event",
"whatsapp_open",
expect.objectContaining({ placement: "floating" }),
);
});