Files
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

113 lines
3.7 KiB
TypeScript

import { render, screen, within } from "@testing-library/react";
import HomeFaq, { FAQ_ITEMS } from "./HomeFaq";
describe("HomeFaq", () => {
it("shows the five questions the site already backs up, closed by default", () => {
const { container } = render(<HomeFaq />);
expect(
screen.getByRole("heading", { name: "Perguntas frequentes" }),
).toBeInTheDocument();
const summaries = Array.from(container.querySelectorAll("summary")).map(
(summary) => summary.textContent?.replace("+", "").trim(),
);
expect(summaries).toEqual([
"Como começa um projeto?",
"Quanto custa?",
"Você atende fora de Santa Catarina?",
"Dá para continuar um sistema que já existe ou que foi feito com IA?",
"O que preciso ter para começar?",
]);
for (const details of container.querySelectorAll("details")) {
expect(details.open).toBe(false);
}
});
it("answers from what the site already says: first conversation, remote in all of Brazil, and the AI review", () => {
render(<HomeFaq />);
expect(
screen.getByText(/Com uma conversa para entender o problema/),
).toBeInTheDocument();
expect(
screen.getByText(
/o atendimento é remoto, para empresas de todo o Brasil/,
),
).toBeInTheDocument();
expect(
screen.getByRole("link", { name: "Ver a revisão de app feito com IA" }),
).toHaveAttribute("href", "/ia");
});
it("does not render the questions the owner has not answered yet", () => {
const { container } = render(<HomeFaq />);
for (const unanswered of [
"O código e os acessos ficam comigo?",
"Em nome de quem ficam domínio e hospedagem?",
"Como funciona o pagamento?",
]) {
// preparadas na lista, com resposta vazia...
expect(FAQ_ITEMS.find((item) => item.question === unanswered)).toEqual({
question: unanswered,
answer: "",
});
// ...e fora da página
expect(screen.queryByText(unanswered)).toBeNull();
}
expect(container.querySelectorAll("details")).toHaveLength(5);
});
it("answers 'Quanto custa?' with the owner's sentence only: no value, no deadline, and no promise that the first talk is free", () => {
render(<HomeFaq />);
const details = screen
.getByText("Quanto custa?")
.closest("details") as HTMLElement;
expect(
within(details).getByText(
"Depois da primeira conversa você recebe escopo, valor e prazo por escrito, antes de qualquer compromisso.",
),
).toBeInTheDocument();
expect(details.textContent).not.toMatch(
/R\$|\d|grátis|gratuit|sem custo|sem compromisso/i,
);
});
it("starts showing a prepared question as soon as it gets an answer", () => {
render(
<HomeFaq
items={[
{
question: "Como funciona o pagamento?",
answer: "Resposta do dono.",
},
{ question: "Ainda sem resposta?", answer: " " },
]}
/>,
);
const details = screen
.getByText("Como funciona o pagamento?")
.closest("details") as HTMLElement;
expect(within(details).getByText("Resposta do dono.")).toBeInTheDocument();
expect(screen.queryByText("Ainda sem resposta?")).toBeNull();
});
it("renders nothing, not even the heading, when no question has an answer", () => {
const { container } = render(
<HomeFaq items={[{ question: "Pergunta?", answer: "" }]} />,
);
expect(container).toBeEmptyDOMElement();
});
it("states no price, deadline, guarantee or payment policy", () => {
const { container } = render(<HomeFaq />);
expect(container.textContent).not.toMatch(
/R\$|garantia|\d+ dias|em até \d+|parcel|entrada de/i,
);
});
});