Home mais curta (hero, vitrine de clientes, sobre, serviços, IA resumida, depoimentos e contato), faixa de clientes e telas reais no hero, sem números do GitHub. Barra de cookies fina, WhatsApp fixo no celular, formulário com e-mail opcional e alternativa por e-mail, página /ia, eventos de e-mail, redes e currículo e conversão do Ads opcional por variável. Rodapé em 3 colunas. Remove componentes órfãos (LighthouseMetrics, Skills, Experience, contagem do GitHub).
217 lines
7.7 KiB
TypeScript
217 lines
7.7 KiB
TypeScript
import { render, screen } from "@testing-library/react";
|
|
import userEvent from "@testing-library/user-event";
|
|
import Contact from "./Contact";
|
|
|
|
beforeEach(() => {
|
|
jest.replaceProperty(process.env, "NODE_ENV", "production");
|
|
localStorage.setItem("evandro.analytics-consent.v1", "granted");
|
|
delete window["ga-disable-G-QTT9QT0T3S"];
|
|
window.gtag = jest.fn();
|
|
jest.spyOn(window, "open").mockImplementation(() => null);
|
|
});
|
|
|
|
afterEach(() => {
|
|
jest.restoreAllMocks();
|
|
localStorage.clear();
|
|
delete window.gtag;
|
|
});
|
|
|
|
it("prepares an isolated WhatsApp draft with the entered project details only after submission", async () => {
|
|
const user = userEvent.setup();
|
|
render(<Contact />);
|
|
|
|
const name = "Ana & João";
|
|
const email = "[email protected]";
|
|
const context =
|
|
"Integrar pedidos & estoque? Objetivo: evitar retrabalho.\nPrazo: outubro #2026";
|
|
await user.type(screen.getByRole("textbox", { name: /nome/i }), name);
|
|
await user.type(screen.getByRole("textbox", { name: /e-mail/i }), email);
|
|
const subject = screen.getByRole("combobox", { name: /tipo de projeto/i });
|
|
await user.selectOptions(subject, "Integrações e automações");
|
|
await user.type(screen.getByRole("textbox", { name: /contexto/i }), context);
|
|
|
|
expect(window.open).not.toHaveBeenCalled();
|
|
expect(window.gtag).not.toHaveBeenCalled();
|
|
await user.click(screen.getByRole("button", { name: /whatsapp/i }));
|
|
|
|
expect(window.open).toHaveBeenCalledTimes(1);
|
|
const [destination, target, features] = jest.mocked(window.open).mock
|
|
.calls[0];
|
|
const url = new URL(String(destination));
|
|
expect(url.origin).toBe("https://wa.me");
|
|
expect(url.pathname).toMatch(/^\/\d+$/);
|
|
expect([...url.searchParams.keys()]).toEqual(["text"]);
|
|
const message = url.searchParams.get("text");
|
|
expect(message).toContain(name);
|
|
expect(message).toContain(email);
|
|
expect(message).toContain("Integrações e automações");
|
|
expect(message).toContain(context);
|
|
expect(target).toBe("_blank");
|
|
expect(features).toBe("noopener,noreferrer");
|
|
expect(window.gtag).toHaveBeenCalledTimes(1);
|
|
expect(window.gtag).toHaveBeenCalledWith("event", "whatsapp_open", {
|
|
send_to: "G-QTT9QT0T3S",
|
|
service: "Contato",
|
|
placement: "contact_form",
|
|
page_path: "/",
|
|
page_location: "https://evandro.dev.br/",
|
|
page_referrer: "",
|
|
});
|
|
});
|
|
|
|
it("does not open WhatsApp while required fields are empty or the email is invalid", async () => {
|
|
const user = userEvent.setup();
|
|
render(<Contact />);
|
|
|
|
const submit = screen.getByRole("button", { name: /whatsapp/i });
|
|
await user.click(submit);
|
|
expect(window.open).not.toHaveBeenCalled();
|
|
|
|
await user.type(screen.getByRole("textbox", { name: /nome/i }), "Ana");
|
|
await user.type(
|
|
screen.getByRole("textbox", { name: /e-mail/i }),
|
|
"email-invalido",
|
|
);
|
|
await user.type(
|
|
screen.getByRole("textbox", { name: /contexto/i }),
|
|
"Organizar pedidos",
|
|
);
|
|
await user.click(submit);
|
|
expect(window.open).not.toHaveBeenCalled();
|
|
expect(window.gtag).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("opens the project draft with analytics unavailable and consent denied", async () => {
|
|
localStorage.setItem("evandro.analytics-consent.v1", "denied");
|
|
delete window.gtag;
|
|
const user = userEvent.setup();
|
|
render(<Contact />);
|
|
await user.type(screen.getByRole("textbox", { name: /nome/i }), "Ana");
|
|
await user.type(
|
|
screen.getByRole("textbox", { name: /e-mail/i }),
|
|
"[email protected]",
|
|
);
|
|
await user.type(
|
|
screen.getByRole("textbox", { name: /contexto/i }),
|
|
"Organizar pedidos",
|
|
);
|
|
await user.click(screen.getByRole("button", { name: /whatsapp/i }));
|
|
expect(window.open).toHaveBeenCalledTimes(1);
|
|
const destination = new URL(
|
|
String(jest.mocked(window.open).mock.calls[0][0]),
|
|
);
|
|
expect(destination.origin).toBe("https://wa.me");
|
|
expect(destination.searchParams.get("text")).toContain("Organizar pedidos");
|
|
});
|
|
|
|
it("lets the visitor skip the e-mail and leaves that line out of the message", async () => {
|
|
const user = userEvent.setup();
|
|
render(<Contact />);
|
|
|
|
expect(screen.getByRole("textbox", { name: /e-mail/i })).not.toBeRequired();
|
|
await user.type(screen.getByRole("textbox", { name: /nome/i }), "Ana");
|
|
await user.type(
|
|
screen.getByRole("textbox", { name: /contexto/i }),
|
|
"Organizar pedidos",
|
|
);
|
|
await user.click(screen.getByRole("button", { name: /whatsapp/i }));
|
|
|
|
expect(window.open).toHaveBeenCalledTimes(1);
|
|
const url = new URL(String(jest.mocked(window.open).mock.calls[0][0]));
|
|
const message = url.searchParams.get("text") ?? "";
|
|
expect(message).toContain("Nome: Ana");
|
|
expect(message).toContain("Tipo de projeto: Site para empresa");
|
|
expect(message).not.toContain("E-mail:");
|
|
});
|
|
|
|
it("offers 'Site para empresa' as the first and default project type", () => {
|
|
render(<Contact />);
|
|
|
|
const subject = screen.getByRole("combobox", { name: /tipo de projeto/i });
|
|
const options = Array.from(subject.querySelectorAll("option")).map(
|
|
(option) => option.value,
|
|
);
|
|
expect(options[0]).toBe("Site para empresa");
|
|
expect(subject).toHaveValue("Site para empresa");
|
|
});
|
|
|
|
it("explains each invalid field in Portuguese and marks it for assistive technology", async () => {
|
|
const user = userEvent.setup();
|
|
render(<Contact />);
|
|
|
|
await user.type(
|
|
screen.getByRole("textbox", { name: /e-mail/i }),
|
|
"email-invalido",
|
|
);
|
|
await user.click(screen.getByRole("button", { name: /whatsapp/i }));
|
|
|
|
const name = screen.getByRole("textbox", { name: /nome/i });
|
|
const email = screen.getByRole("textbox", { name: /e-mail/i });
|
|
const message = screen.getByRole("textbox", { name: /contexto/i });
|
|
for (const field of [name, email, message]) {
|
|
expect(field).toHaveAttribute("aria-invalid", "true");
|
|
}
|
|
expect(name).toHaveAccessibleDescription(
|
|
"Informe seu nome para eu saber com quem falo.",
|
|
);
|
|
expect(email).toHaveAccessibleDescription(
|
|
"Confira o e-mail: ele precisa ter o formato [email protected].",
|
|
);
|
|
expect(message).toHaveAccessibleDescription(
|
|
/Conte em poucas palavras o que você precisa resolver\./,
|
|
);
|
|
expect(name).toHaveFocus();
|
|
expect(window.open).not.toHaveBeenCalled();
|
|
|
|
await user.type(name, "Ana");
|
|
expect(name).not.toHaveAttribute("aria-invalid");
|
|
expect(
|
|
screen.queryByText("Informe seu nome para eu saber com quem falo."),
|
|
).toBeNull();
|
|
});
|
|
|
|
it("offers the same text by e-mail, below the WhatsApp button", async () => {
|
|
const user = userEvent.setup();
|
|
render(<Contact />);
|
|
|
|
await user.type(screen.getByRole("textbox", { name: /nome/i }), "Ana");
|
|
await user.type(
|
|
screen.getByRole("textbox", { name: /contexto/i }),
|
|
"Organizar pedidos & estoque",
|
|
);
|
|
const submit = screen.getByRole("button", { name: /whatsapp/i });
|
|
const mail = screen.getByRole("link", {
|
|
name: "Prefere e-mail? Enviar por e-mail",
|
|
});
|
|
expect(
|
|
submit.compareDocumentPosition(mail) & Node.DOCUMENT_POSITION_FOLLOWING,
|
|
).toBeTruthy();
|
|
|
|
const href = mail.getAttribute("href") ?? "";
|
|
expect(href.startsWith("mailto:[email protected]?")).toBe(true);
|
|
const url = new URL(href);
|
|
expect(url.searchParams.get("subject")).toBe("Projeto pelo site");
|
|
const body = url.searchParams.get("body") ?? "";
|
|
expect(body).toContain("Nome: Ana");
|
|
expect(body).toContain("Organizar pedidos & estoque");
|
|
expect(body).toContain("Tipo de projeto: Site para empresa");
|
|
|
|
await user.click(mail);
|
|
expect(window.gtag).toHaveBeenCalledWith(
|
|
"event",
|
|
"email_click",
|
|
expect.objectContaining({ placement: "contact_form" }),
|
|
);
|
|
});
|
|
|
|
it("does not call the direct WhatsApp row 'Contato'", () => {
|
|
render(<Contact />);
|
|
|
|
const direct = screen.getByRole("link", { name: /Prefere conversar direto/ });
|
|
const text = new URL(direct.getAttribute("href") ?? "").searchParams.get(
|
|
"text",
|
|
);
|
|
expect(text).toContain("Quero conversar sobre um projeto.");
|
|
expect(text).not.toMatch(/sobre Contato/);
|
|
});
|