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.
This commit is contained in:
Evandro Fonseca Junior committed 2026-10-14 17:01:36 -03:00
1 parent de6af74a0c
commit f8545cd442
44 files changed
+2286 -571

No files matched your search

+102
View File
@@ -0,0 +1,102 @@
import { render, screen, within } from "@testing-library/react";
import HeroWork from "./HeroWork";
/**
* Os pôsteres do hero: pelo next/image com `sizes` do tamanho em que aparecem, sempre lazy e com prioridade baixa
* (o título é o LCP), e a moldura de celular pequena para as telas abaixo de 1024 px.
*/
const original = (image: Element) =>
new URL(image.getAttribute("src") ?? "", "http://localhost").searchParams.get(
"url",
);
describe("HeroWork: pôsteres no tamanho certo", () => {
it("serves the three posters through the image optimizer, each with a `sizes` that matches how it shows", () => {
render(<HeroWork />);
const desktop = within(screen.getByTestId("hero-work")).getAllByRole("img");
expect(desktop).toHaveLength(2);
expect(original(desktop[0])).toBe("/videos/sev-modaintima/desktop.webp");
expect(original(desktop[1])).toBe("/videos/as-servicos/mobile.webp");
// 1440x900 e 390x844 cheios para quadros de ~491x307 e ~130x299 era banda de sobra
expect(desktop[0]).toHaveAttribute(
"sizes",
"(min-width: 1400px) 500px, 36vw",
);
expect(desktop[1]).toHaveAttribute(
"sizes",
"(min-width: 1400px) 136px, 10vw",
);
const small = within(screen.getByTestId("hero-work-mobile")).getByRole(
"img",
);
expect(original(small)).toBe("/videos/sev-modaintima/mobile.webp");
expect(small).toHaveAttribute("sizes", "84px");
});
it("keeps the dimensions of the files, so the frames reserve their space before the images load", () => {
render(<HeroWork />);
const [browser, phone] = within(
screen.getByTestId("hero-work"),
).getAllByRole("img");
expect(browser).toHaveAttribute("width", "1440");
expect(browser).toHaveAttribute("height", "900");
expect(phone).toHaveAttribute("width", "390");
expect(phone).toHaveAttribute("height", "844");
});
it("never asks for a high priority: lazy, low fetch priority and no preload, so the headline stays the LCP", () => {
const { container } = render(<HeroWork />);
const images = container.querySelectorAll("img");
expect(images).toHaveLength(3);
for (const image of images) {
expect(image).toHaveAttribute("loading", "lazy");
expect(image).toHaveAttribute("fetchpriority", "low");
}
// sem <link rel="preload">: o next/image só o insere com `preload` (ou fetchPriority="high")
expect(document.head.querySelector('link[rel="preload"]')).toBeNull();
});
});
describe("HeroWork: moldura de celular pequena", () => {
it("shows a 180px phone with the SEV mobile poster only below 1024px, while the column stays desktop-only", () => {
render(<HeroWork />);
const column = screen.getByTestId("hero-work");
expect(column).toHaveClass("hidden", "lg:block");
const small = screen.getByTestId("hero-work-mobile");
expect(small).toHaveClass("lg:hidden");
const phone = small.querySelector(".ds-phone") as HTMLElement;
expect(phone.style.height).toBe("180px");
expect(phone).toContainElement(within(small).getByRole("img"));
expect(within(small).getByRole("img")).toHaveAccessibleName(
"Site da SEV Moda Íntima aberto no celular",
);
expect(
within(small).getByText(/Site da SEV Moda Íntima, como aparece/),
).toBeInTheDocument();
});
it("is not interactive: the frames only show the work", () => {
render(<HeroWork />);
expect(screen.queryByRole("link")).toBeNull();
expect(screen.getByTestId("hero-work-mobile")).toHaveClass(
"pointer-events-none",
);
expect(screen.getByTestId("hero-work")).toHaveClass("pointer-events-none");
});
it("passes the address to the browser bar of the desktop frame", () => {
render(<HeroWork url={{ text: "sevmodaintima.com.br", secure: true }} />);
expect(
within(screen.getByTestId("hero-work")).getByText("sevmodaintima.com.br"),
).toBeInTheDocument();
});
});