feat(home): posicionamento com prova, menu sem recarregar e WhatsApp em todo o site
H1 'Sites e sistemas que trazem pedidos pelo WhatsApp e tiram sua operação da planilha.'; HeroWork maior com a SEV; menu Cases, Serviços, Como trabalho, Sobre e IA com next/link e pré-carregamento no hover; menu do celular em tela cheia; WhatsApp flutuante e no rodapé com a mensagem da rota; rodapé em todas as páginas; FAQ da home; /ia como oferta de revisão de app feito com IA; formulário com noscript, validação do mailto e plano B do pop-up; cartões sem foco indevido; sem a fonte Monument.
This commit is contained in:
1 parent
e80f73d075
commit
10f170abf4
56 files changed
+3099
-906
No files matched your search
@@ -0,0 +1,154 @@
|
||||
import { act, fireEvent, render, screen } from "@testing-library/react";
|
||||
import { usePathname } from "next/navigation";
|
||||
import { Header } from "./Header";
|
||||
|
||||
/**
|
||||
* Testes de caminhos-base de Header (complemento de Header.test.tsx): a barra que escurece com a rolagem,
|
||||
* as teclas do menu do celular que não o fecham e a rota ausente (fora do roteador).
|
||||
*/
|
||||
|
||||
jest.mock("next/navigation", () => ({ usePathname: jest.fn() }));
|
||||
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>
|
||||
),
|
||||
}));
|
||||
|
||||
function setScrollY(value: number) {
|
||||
Object.defineProperty(window, "scrollY", {
|
||||
value,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
});
|
||||
}
|
||||
|
||||
const header = () => screen.getByRole("banner");
|
||||
const isDark = () => header().className.includes("backdrop-blur-md");
|
||||
|
||||
beforeEach(() => {
|
||||
jest.mocked(usePathname).mockReturnValue("/");
|
||||
setScrollY(0);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.restoreAllMocks();
|
||||
setScrollY(0);
|
||||
});
|
||||
|
||||
describe("Header: rolagem", () => {
|
||||
it("path: page at the top -> transparent border and solid background", () => {
|
||||
render(<Header />);
|
||||
|
||||
expect(isDark()).toBe(false);
|
||||
expect(header()).toHaveClass("border-transparent");
|
||||
});
|
||||
|
||||
it("path: scrolling down and back to the top -> blurred bar, then the plain one again", () => {
|
||||
render(<Header />);
|
||||
|
||||
setScrollY(120);
|
||||
act(() => {
|
||||
window.dispatchEvent(new Event("scroll"));
|
||||
});
|
||||
expect(isDark()).toBe(true);
|
||||
expect(header()).not.toHaveClass("border-transparent");
|
||||
|
||||
// outro evento de rolagem sem mudar de lado: nada muda
|
||||
setScrollY(300);
|
||||
act(() => {
|
||||
window.dispatchEvent(new Event("scroll"));
|
||||
});
|
||||
expect(isDark()).toBe(true);
|
||||
|
||||
setScrollY(0);
|
||||
act(() => {
|
||||
window.dispatchEvent(new Event("scroll"));
|
||||
});
|
||||
expect(isDark()).toBe(false);
|
||||
});
|
||||
|
||||
it("path: page already scrolled when the header mounts -> starts with the blurred bar", () => {
|
||||
setScrollY(500);
|
||||
render(<Header />);
|
||||
|
||||
expect(isDark()).toBe(true);
|
||||
});
|
||||
|
||||
it("path: unmount -> the scroll listener is removed", () => {
|
||||
const remove = jest.spyOn(window, "removeEventListener");
|
||||
const { unmount } = render(<Header />);
|
||||
|
||||
unmount();
|
||||
|
||||
expect(remove).toHaveBeenCalledWith("scroll", expect.any(Function));
|
||||
});
|
||||
});
|
||||
|
||||
describe("Header: teclado no menu do celular", () => {
|
||||
it("path: a key other than Escape inside the open menu -> the menu stays open and the key is not blocked", () => {
|
||||
render(<Header />);
|
||||
fireEvent.click(screen.getByRole("button", { name: "Abrir menu" }));
|
||||
const menu = screen.getByRole("navigation", { name: "Navegação mobile" });
|
||||
|
||||
// fireEvent devolve false só quando o evento foi cancelado (preventDefault)
|
||||
expect(fireEvent.keyDown(menu, { key: "Tab" })).toBe(true);
|
||||
expect(fireEvent.keyDown(menu, { key: "Enter" })).toBe(true);
|
||||
|
||||
expect(screen.getByRole("button", { name: "Fechar menu" })).toHaveAttribute(
|
||||
"aria-expanded",
|
||||
"true",
|
||||
);
|
||||
expect(
|
||||
screen.getByRole("navigation", { name: "Navegação mobile" }),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("path: Escape on the menu button while the menu is open -> closes it and keeps the focus on the button", () => {
|
||||
render(<Header />);
|
||||
const button = screen.getByRole("button", { name: "Abrir menu" });
|
||||
fireEvent.click(button);
|
||||
act(() => button.focus());
|
||||
|
||||
expect(fireEvent.keyDown(button, { key: "Escape" })).toBe(false);
|
||||
|
||||
const closed = screen.getByRole("button", { name: "Abrir menu" });
|
||||
expect(closed).toHaveAttribute("aria-expanded", "false");
|
||||
expect(closed).toHaveFocus();
|
||||
expect(
|
||||
screen.queryByRole("navigation", { name: "Navegação mobile" }),
|
||||
).toBeNull();
|
||||
});
|
||||
|
||||
it("path: Escape while the menu is already closed -> nothing opens", () => {
|
||||
render(<Header />);
|
||||
const button = screen.getByRole("button", { name: "Abrir menu" });
|
||||
|
||||
fireEvent.keyDown(button, { key: "Escape" });
|
||||
|
||||
expect(button).toHaveAttribute("aria-expanded", "false");
|
||||
expect(
|
||||
screen.queryByRole("navigation", { name: "Navegação mobile" }),
|
||||
).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe("Header: rota ausente", () => {
|
||||
it("path: usePathname gives null (outside the router) -> the header button goes to the home form", () => {
|
||||
jest.mocked(usePathname).mockReturnValue(null as unknown as string);
|
||||
render(<Header />);
|
||||
|
||||
expect(
|
||||
screen.getByRole("link", { name: "Falar sobre meu projeto" }),
|
||||
).toHaveAttribute("href", "/#contact");
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user