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.
160 lines
5.8 KiB
TypeScript
160 lines
5.8 KiB
TypeScript
import { render, screen } from "@testing-library/react";
|
|
|
|
import Hero from "./Hero";
|
|
|
|
const clients = [
|
|
{ id: "sev-modaintima", name: "SEV Moda Íntima" },
|
|
{ id: "lavacao-047", name: "Lavação 047" },
|
|
{ id: "sc-plus", name: "SC Plus Aquicultura" },
|
|
{ id: "as-servicos", name: "AS Serviços" },
|
|
];
|
|
|
|
describe("Hero", () => {
|
|
beforeEach(() => {
|
|
jest.spyOn(window, "open").mockImplementation(() => null);
|
|
});
|
|
|
|
afterEach(() => {
|
|
jest.restoreAllMocks();
|
|
});
|
|
|
|
it("opens WhatsApp straight from the main button, with the subject of the home, while 'Ver projetos entregues' stays secondary", () => {
|
|
render(<Hero />);
|
|
|
|
const contactLink = screen.getByRole("link", {
|
|
name: /conversar/i,
|
|
});
|
|
expect(contactLink).toHaveAccessibleName("Conversar sobre meu projeto");
|
|
expect(contactLink.getAttribute("href")).toMatch(
|
|
/^https:\/\/wa\.me\/\d+\?/,
|
|
);
|
|
expect(contactLink).toHaveAttribute("target", "_blank");
|
|
expect(contactLink).toHaveAttribute("rel", "noopener noreferrer");
|
|
const text = new URL(
|
|
contactLink.getAttribute("href") ?? "",
|
|
).searchParams.get("text");
|
|
expect(text).toBe(
|
|
"Olá, Evandro! Quero conversar sobre um site ou sistema para a minha empresa. Contato iniciado pelo site evandro.dev.br.",
|
|
);
|
|
// o botão primário é o único com o fundo limão; o secundário é contorno
|
|
expect(contactLink.className).toContain("bg-[var(--slash)]");
|
|
const projects = screen.getByRole("link", { name: /ver projetos/i });
|
|
expect(projects).toHaveAttribute("href", "/cases");
|
|
expect(projects.className).not.toContain("bg-[var(--slash)]");
|
|
expect(projects).not.toHaveAttribute("target");
|
|
});
|
|
|
|
it("keeps the headline as one readable sentence despite the per-word animation", () => {
|
|
render(<Hero />);
|
|
|
|
expect(screen.getByRole("heading", { level: 1 })).toHaveTextContent(
|
|
"Sites e sistemas que trazem pedidos pelo WhatsApp e tiram sua operação da planilha.",
|
|
);
|
|
});
|
|
|
|
it("highlights only the short stretch 'pedidos pelo WhatsApp' in lime", () => {
|
|
render(<Hero />);
|
|
|
|
const highlighted = Array.from(
|
|
screen.getByRole("heading", { level: 1 }).querySelectorAll(".hero-word"),
|
|
)
|
|
.filter((word) => word.className.includes("text-[var(--slash)]"))
|
|
.map((word) => word.textContent);
|
|
expect(highlighted).toEqual(["pedidos", "pelo", "WhatsApp"]);
|
|
});
|
|
|
|
it("keeps the single LinkedIn profile and the e-mail, with no GitHub link", () => {
|
|
render(<Hero />);
|
|
|
|
expect(screen.getByRole("link", { name: "LinkedIn" })).toHaveAttribute(
|
|
"href",
|
|
"https://www.linkedin.com/in/evandrodevbr/",
|
|
);
|
|
expect(screen.getByRole("link", { name: "Email" })).toHaveAttribute(
|
|
"href",
|
|
"mailto:[email protected]",
|
|
);
|
|
const linkedin = screen.getByRole("link", { name: "LinkedIn" });
|
|
expect(linkedin).toHaveAttribute("target", "_blank");
|
|
expect(linkedin).toHaveAttribute("rel", "noopener noreferrer");
|
|
expect(screen.queryByRole("link", { name: "GitHub" })).toBeNull();
|
|
for (const link of screen.getAllByRole("link")) {
|
|
expect(link.getAttribute("href") ?? "").not.toMatch(/github\.com/);
|
|
}
|
|
});
|
|
|
|
it("states who the work is for and how the contact works", () => {
|
|
render(<Hero />);
|
|
|
|
expect(
|
|
screen.getByText(
|
|
"Do catálogo com pedido no WhatsApp ao painel da sua equipe. Você fala direto com quem desenvolve.",
|
|
),
|
|
).toBeInTheDocument();
|
|
expect(screen.getByRole("link", { name: /conversar/i })).toHaveAttribute(
|
|
"id",
|
|
"hero-contact-cta",
|
|
);
|
|
});
|
|
|
|
it("shows only the years of experience, with no repository numbers", () => {
|
|
render(<Hero stats={{ years: 5 }} />);
|
|
|
|
expect(screen.getByText("anos de experiência")).toBeInTheDocument();
|
|
expect(screen.getByText("5")).toBeInTheDocument();
|
|
expect(screen.queryByRole("term")).toBeNull();
|
|
expect(screen.queryByText(/projetos desenvolvidos/i)).toBeNull();
|
|
expect(screen.queryByText(/públicos|privados|mais de/i)).toBeNull();
|
|
});
|
|
|
|
it("lists the delivered clients, each linking to its case", () => {
|
|
render(<Hero clients={clients} />);
|
|
|
|
expect(screen.getByText("Já entregue para:")).toBeInTheDocument();
|
|
for (const { id, name } of clients) {
|
|
expect(screen.getByRole("link", { name })).toHaveAttribute(
|
|
"href",
|
|
`/cases/${id}`,
|
|
);
|
|
}
|
|
});
|
|
|
|
it("renders the work column with lazy posters that stay out of small screens", () => {
|
|
render(<Hero clients={clients} />);
|
|
|
|
const work = screen.getByTestId("hero-work");
|
|
expect(work).toHaveClass("hidden", "lg:block");
|
|
const images = work.querySelectorAll("img");
|
|
expect(images).toHaveLength(2);
|
|
for (const image of images) {
|
|
expect(image).toHaveAttribute("loading", "lazy");
|
|
}
|
|
// pelo otimizador do next/image: o endereço original vai no parâmetro `url`
|
|
const original = (image: Element) =>
|
|
new URL(
|
|
image.getAttribute("src") ?? "",
|
|
"http://localhost",
|
|
).searchParams.get("url");
|
|
expect(original(images[0])).toBe("/videos/sev-modaintima/desktop.webp");
|
|
expect(images[0]).toHaveAccessibleName(
|
|
"Site da SEV Moda Íntima aberto no computador",
|
|
);
|
|
expect(original(images[1])).toBe("/videos/as-servicos/mobile.webp");
|
|
});
|
|
|
|
it("shows a small phone frame with the mobile poster of SEV below the buttons on small screens", () => {
|
|
render(<Hero clients={clients} />);
|
|
|
|
const small = screen.getByTestId("hero-work-mobile");
|
|
expect(small).toHaveClass("lg:hidden");
|
|
const image = small.querySelector("img") as HTMLImageElement;
|
|
expect(image).toHaveAccessibleName(
|
|
"Site da SEV Moda Íntima aberto no celular",
|
|
);
|
|
const buttons = screen.getByRole("link", { name: /ver projetos/i });
|
|
expect(
|
|
buttons.compareDocumentPosition(small) & Node.DOCUMENT_POSITION_FOLLOWING,
|
|
).toBeTruthy();
|
|
});
|
|
});
|