Projetos sobem sobre o palco do hero e o celular tem hero de uma tela sem mergulho; passagens com animation-timeline em CSS, sem GSAP nem motion; troca da vitrine sem texto duplicado; moldura de celular só com pôster e scrub reencodado em 960 px (9,4 para 5,2 MB); 3D monta na primeira interação e para quando parado; transição entre páginas do mesmo segmento; foco em anel duplo; texto de 15 px no celular; código morto removido.
103 lines
3.2 KiB
TypeScript
103 lines
3.2 KiB
TypeScript
import { act, render, screen } from "@testing-library/react";
|
|
import CinematicOverlays from "./CinematicOverlays";
|
|
|
|
let mockPathname = "/";
|
|
jest.mock("next/navigation", () => ({ usePathname: () => mockPathname }));
|
|
|
|
// o grão vem por import dinâmico (pacote próprio): aqui ele vira um marcador, e o contador diz se o pacote foi pedido
|
|
const mockLoaded = jest.fn();
|
|
jest.mock("./FilmGrain", () => {
|
|
mockLoaded();
|
|
return {
|
|
__esModule: true,
|
|
default: function MockGrain() {
|
|
return <div data-testid="grain" />;
|
|
},
|
|
};
|
|
});
|
|
|
|
/** O import dinâmico do grão resolve em microtarefas. */
|
|
const loaded = () =>
|
|
act(async () => {
|
|
for (let i = 0; i < 5; i++) await Promise.resolve();
|
|
});
|
|
|
|
const original = window.matchMedia;
|
|
|
|
function mockMedia(active: string[] = []) {
|
|
window.matchMedia = jest.fn().mockImplementation((query: string) => ({
|
|
matches: active.includes(query),
|
|
media: query,
|
|
addEventListener() {},
|
|
removeEventListener() {},
|
|
}));
|
|
}
|
|
|
|
beforeEach(() => {
|
|
mockPathname = "/";
|
|
mockMedia();
|
|
});
|
|
|
|
afterEach(() => {
|
|
window.matchMedia = original;
|
|
});
|
|
|
|
/*
|
|
* Os três primeiros testes vêm antes de qualquer um que carregue o grão: o módulo, depois de pedido uma vez, fica
|
|
* no registro do Jest, então "o pacote nunca foi pedido" só vale enquanto ninguém o pediu.
|
|
*/
|
|
describe("CinematicOverlays: where the grain package is never requested", () => {
|
|
it("does not request the grain on a touch screen (nothing invisible left measuring the page); the progress bar stays", async () => {
|
|
mockMedia(["(pointer: coarse)"]);
|
|
const { container } = render(<CinematicOverlays />);
|
|
await loaded();
|
|
|
|
expect(mockLoaded).not.toHaveBeenCalled();
|
|
expect(screen.queryByTestId("grain")).toBeNull();
|
|
// a barra de progresso é CSS puro: continua
|
|
expect(container.querySelector(".ds-scroll-bar")).toBeInTheDocument();
|
|
});
|
|
|
|
it("does not request the grain for users who prefer reduced motion", async () => {
|
|
mockMedia(["(prefers-reduced-motion: reduce)"]);
|
|
render(<CinematicOverlays />);
|
|
await loaded();
|
|
|
|
expect(mockLoaded).not.toHaveBeenCalled();
|
|
expect(screen.queryByTestId("grain")).toBeNull();
|
|
});
|
|
|
|
it("shows nothing, and requests nothing, on the other pages", async () => {
|
|
mockPathname = "/cases";
|
|
const { container } = render(<CinematicOverlays />);
|
|
await loaded();
|
|
|
|
expect(mockLoaded).not.toHaveBeenCalled();
|
|
expect(screen.queryByTestId("grain")).toBeNull();
|
|
expect(container).toBeEmptyDOMElement();
|
|
});
|
|
});
|
|
|
|
describe("CinematicOverlays: with a mouse", () => {
|
|
it("shows the scroll progress at once and the film grain as soon as its package arrives", async () => {
|
|
const { container } = render(<CinematicOverlays />);
|
|
|
|
// a barra vem no HTML; o grão só depois do import
|
|
expect(container.querySelector(".ds-scroll-bar")).toBeInTheDocument();
|
|
expect(screen.queryByTestId("grain")).toBeNull();
|
|
|
|
await loaded();
|
|
expect(screen.getByTestId("grain")).toBeInTheDocument();
|
|
expect(mockLoaded).toHaveBeenCalled();
|
|
});
|
|
|
|
it("does not mount the grain if the page unmounts while its package loads", async () => {
|
|
const { unmount } = render(<CinematicOverlays />);
|
|
unmount();
|
|
|
|
await loaded();
|
|
|
|
expect(screen.queryByTestId("grain")).toBeNull();
|
|
});
|
|
});
|