fix(home): oferta leva ao formulário, ofertas logo depois da vitrine
O botão das ofertas rola até o contato com o tipo marcado e não quebra mais as âncoras; ofertas com o que você recebe e primeiro passo, logo depois da vitrine; bloco de IA fora da home; cartão largo da SEV no hero do celular; separador da faixa de clientes só entre itens; favicon de 13 para 1,8 KB; pré-carregamento só no hover e nunca de /#... fora da home; X do menu e cabeçalho opaco; WhatsApp com "o case"; /ia com 3 passos e modelo de relatório ilustrativo; Sobre alinhado ao currículo; CSP Report-Only com os domínios do Ads.
This commit is contained in:
1 parent
fc44a69fb3
commit
9e2d65a926
51 files changed
+1730
-420
No files matched your search
@@ -1,6 +1,24 @@
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import { fireEvent, render, screen } from "@testing-library/react";
|
||||
|
||||
import Hero from "./Hero";
|
||||
import { HERO_CAPTIONS } from "./hero-captions";
|
||||
|
||||
// o Link do Next, trocado por um <a> que mostra o `prefetch` recebido: HoverPrefetchLink começa em false
|
||||
jest.mock("next/link", () => ({
|
||||
__esModule: true,
|
||||
default: ({
|
||||
children,
|
||||
prefetch,
|
||||
...props
|
||||
}: { children?: React.ReactNode; prefetch?: boolean | null } & Record<
|
||||
string,
|
||||
unknown
|
||||
>) => (
|
||||
<a data-prefetch={String(prefetch)} {...props}>
|
||||
{children}
|
||||
</a>
|
||||
),
|
||||
}));
|
||||
|
||||
const clients = [
|
||||
{ id: "sev-modaintima", name: "SEV Moda Íntima" },
|
||||
@@ -22,9 +40,9 @@ describe("Hero", () => {
|
||||
render(<Hero />);
|
||||
|
||||
const contactLink = screen.getByRole("link", {
|
||||
name: /conversar/i,
|
||||
name: /falar sobre meu projeto/i,
|
||||
});
|
||||
expect(contactLink).toHaveAccessibleName("Conversar sobre meu projeto");
|
||||
expect(contactLink).toHaveAccessibleName("Falar sobre meu projeto");
|
||||
expect(contactLink.getAttribute("href")).toMatch(
|
||||
/^https:\/\/wa\.me\/\d+\?/,
|
||||
);
|
||||
@@ -88,13 +106,12 @@ describe("Hero", () => {
|
||||
|
||||
expect(
|
||||
screen.getByText(
|
||||
"Do catálogo com pedido no WhatsApp ao painel da sua equipe. Você fala direto com quem desenvolve.",
|
||||
"Do catálogo com pedido no WhatsApp ao painel da sua equipe. Você fala com quem desenvolve.",
|
||||
),
|
||||
).toBeInTheDocument();
|
||||
expect(screen.getByRole("link", { name: /conversar/i })).toHaveAttribute(
|
||||
"id",
|
||||
"hero-contact-cta",
|
||||
);
|
||||
expect(
|
||||
screen.getByRole("link", { name: /falar sobre meu projeto/i }),
|
||||
).toHaveAttribute("id", "hero-contact-cta");
|
||||
});
|
||||
|
||||
it("shows only the years of experience, with no repository numbers", () => {
|
||||
@@ -142,18 +159,64 @@ describe("Hero", () => {
|
||||
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", () => {
|
||||
it("shows a wide card to the SEV case 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",
|
||||
);
|
||||
expect(small).toHaveAttribute("href", "/cases/sev-modaintima");
|
||||
expect(small).toHaveAccessibleName("Site da SEV Moda Íntima Ver case");
|
||||
const buttons = screen.getByRole("link", { name: /ver projetos/i });
|
||||
expect(
|
||||
buttons.compareDocumentPosition(small) & Node.DOCUMENT_POSITION_FOLLOWING,
|
||||
).toBeTruthy();
|
||||
});
|
||||
|
||||
it("keeps the label above the headline in the display font, so the mono font is not downloaded before the LCP", () => {
|
||||
render(<Hero />);
|
||||
|
||||
const label = screen.getByText(
|
||||
/Evandro Fonseca Junior · Engenharia de software/,
|
||||
);
|
||||
expect(label.className).toContain("font-display");
|
||||
expect(label.className).not.toContain("font-mono");
|
||||
});
|
||||
|
||||
it("uses no mono font anywhere in the first fold (label, years, scroll cue)", () => {
|
||||
const { container } = render(
|
||||
<Hero clients={clients} stats={{ years: 5 }} />,
|
||||
);
|
||||
|
||||
expect(container.querySelector(".font-mono")).toBeNull();
|
||||
});
|
||||
|
||||
it("pre-fetches 'Ver projetos entregues', the clients and the SEV card only when reached (hover, focus or touch), never by viewport", () => {
|
||||
render(<Hero clients={clients} />);
|
||||
|
||||
const links = [
|
||||
screen.getByRole("link", { name: /ver projetos/i }),
|
||||
screen.getByRole("link", { name: "SEV Moda Íntima" }),
|
||||
screen.getByTestId("hero-work-mobile"),
|
||||
];
|
||||
for (const link of links) {
|
||||
expect(link).toHaveAttribute("data-prefetch", "false");
|
||||
fireEvent.mouseEnter(link);
|
||||
expect(link).toHaveAttribute("data-prefetch", "null");
|
||||
}
|
||||
});
|
||||
|
||||
it("writes the range of each dive caption (--from and --to, from hero-captions.ts) on its own item, in order", () => {
|
||||
const { container } = render(<Hero clients={clients} />);
|
||||
|
||||
const items = Array.from(
|
||||
container.querySelectorAll<HTMLElement>(".hero-captions li"),
|
||||
);
|
||||
expect(items).toHaveLength(HERO_CAPTIONS.length);
|
||||
items.forEach((item, index) => {
|
||||
const { title, text, from, to } = HERO_CAPTIONS[index];
|
||||
expect(item).toHaveTextContent(`${title}${text}`);
|
||||
expect(item.style.getPropertyValue("--from")).toBe(String(from));
|
||||
expect(item.style.getPropertyValue("--to")).toBe(String(to));
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user