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:
1 parent
de6af74a0c
commit
f8545cd442
44 files changed
+2286
-571
No files matched your search
@@ -0,0 +1,149 @@
|
||||
import { act, render, screen, within } from "@testing-library/react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import Contact from "./Contact";
|
||||
import { CONTACT_KIND_EVENT } from "./contact-subject";
|
||||
|
||||
/**
|
||||
* O tipo de projeto do formulário vindo da oferta escolhida na home: pelo evento que o botão da oferta dispara
|
||||
* (na própria home) ou pelo `?tipo=` do endereço (ao chegar de outra página), mais a opção de revisão de sistema.
|
||||
*/
|
||||
|
||||
const subjectSelect = () =>
|
||||
screen.getByRole("combobox", {
|
||||
name: /tipo de projeto/i,
|
||||
}) as HTMLSelectElement;
|
||||
|
||||
const choose = (kind: unknown) =>
|
||||
act(() => {
|
||||
window.dispatchEvent(new CustomEvent(CONTACT_KIND_EVENT, { detail: kind }));
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
window.history.replaceState(null, "", "/");
|
||||
});
|
||||
|
||||
describe("Contact: opções do tipo de projeto", () => {
|
||||
it("lists 'Revisão ou diagnóstico de sistema' and keeps 'Ainda preciso definir' first and selected", () => {
|
||||
render(<Contact />);
|
||||
|
||||
const options = within(subjectSelect()).getAllByRole("option");
|
||||
expect(options.map((option) => option.textContent)).toEqual([
|
||||
"Ainda preciso definir",
|
||||
"Site para empresa",
|
||||
"Sistemas web sob medida",
|
||||
"Aplicativos",
|
||||
"Integrações e automações",
|
||||
"Evolução de um sistema existente",
|
||||
"Revisão ou diagnóstico de sistema",
|
||||
]);
|
||||
expect(subjectSelect()).toHaveValue("Ainda preciso definir");
|
||||
});
|
||||
|
||||
it("puts the review type in the message that goes to WhatsApp and e-mail", async () => {
|
||||
const user = userEvent.setup({ delay: null });
|
||||
render(<Contact />);
|
||||
|
||||
await user.selectOptions(
|
||||
subjectSelect(),
|
||||
"Revisão ou diagnóstico de sistema",
|
||||
);
|
||||
|
||||
const mail = screen.getByRole("link", { name: /e-mail/i });
|
||||
const body = new URL(mail.getAttribute("href") ?? "").searchParams.get(
|
||||
"body",
|
||||
);
|
||||
expect(body).toContain(
|
||||
"Tipo de projeto: Revisão ou diagnóstico de sistema",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Contact: tipo escolhido pela oferta", () => {
|
||||
it.each([
|
||||
["site", "Site para empresa"],
|
||||
["sistema", "Sistemas web sob medida"],
|
||||
["revisao", "Revisão ou diagnóstico de sistema"],
|
||||
])("the offer event with %s selects %s", (kind, option) => {
|
||||
render(<Contact />);
|
||||
|
||||
choose(kind);
|
||||
|
||||
expect(subjectSelect()).toHaveValue(option);
|
||||
});
|
||||
|
||||
it("a later offer replaces the earlier choice, and a person's own pick is replaced by the offer too", async () => {
|
||||
const user = userEvent.setup({ delay: null });
|
||||
render(<Contact />);
|
||||
|
||||
choose("site");
|
||||
await user.selectOptions(subjectSelect(), "Aplicativos");
|
||||
expect(subjectSelect()).toHaveValue("Aplicativos");
|
||||
choose("revisao");
|
||||
|
||||
expect(subjectSelect()).toHaveValue("Revisão ou diagnóstico de sistema");
|
||||
});
|
||||
|
||||
it.each([
|
||||
"desconhecido",
|
||||
"constructor",
|
||||
"__proto__",
|
||||
"",
|
||||
undefined,
|
||||
null,
|
||||
42,
|
||||
])("an unknown type %p changes nothing", (kind) => {
|
||||
render(<Contact />);
|
||||
choose("sistema");
|
||||
|
||||
choose(kind);
|
||||
|
||||
expect(subjectSelect()).toHaveValue("Sistemas web sob medida");
|
||||
});
|
||||
|
||||
it("reads ?tipo= from the address when the form mounts (arrival from another page)", () => {
|
||||
window.history.replaceState(null, "", "/?tipo=revisao#contact");
|
||||
|
||||
render(<Contact />);
|
||||
|
||||
expect(subjectSelect()).toHaveValue("Revisão ou diagnóstico de sistema");
|
||||
});
|
||||
|
||||
it("ignores an unknown ?tipo= in the address and keeps the default", () => {
|
||||
window.history.replaceState(null, "", "/?tipo=outra-coisa");
|
||||
|
||||
render(<Contact />);
|
||||
|
||||
expect(subjectSelect()).toHaveValue("Ainda preciso definir");
|
||||
});
|
||||
|
||||
it("stops listening when it unmounts", () => {
|
||||
const add = jest.spyOn(window, "addEventListener");
|
||||
const remove = jest.spyOn(window, "removeEventListener");
|
||||
const { unmount } = render(<Contact />);
|
||||
const registered = add.mock.calls.find(
|
||||
([type]) => type === CONTACT_KIND_EVENT,
|
||||
);
|
||||
expect(registered).toBeDefined();
|
||||
|
||||
unmount();
|
||||
|
||||
expect(remove).toHaveBeenCalledWith(CONTACT_KIND_EVENT, registered?.[1]);
|
||||
add.mockRestore();
|
||||
remove.mockRestore();
|
||||
});
|
||||
});
|
||||
|
||||
describe("Contact: texto da seção", () => {
|
||||
it("keeps 'conversar direto' only on the WhatsApp line, with no other 'fala direto' phrasing", () => {
|
||||
const { container } = render(<Contact />);
|
||||
|
||||
expect(screen.getByText("Prefere conversar direto?")).toBeInTheDocument();
|
||||
expect(container.textContent).not.toMatch(/fala direto|fale direto/i);
|
||||
expect(
|
||||
screen.getByRole("heading", {
|
||||
level: 3,
|
||||
name: "Como a conversa começa.",
|
||||
}),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user