import { act, fireEvent, render, screen } from "@testing-library/react"; import { MOBILE_QUERY } from "@/lib/device"; import { getFeaturedProjects } from "@/lib/portfolio-data"; import { ProjectReel } from "./ProjectReel"; import { ShowcaseProvider, useShowcaseOpen } from "./ShowcaseProvider"; import { PINNED_QUERY } from "./useShowcaseMedia"; /* * A vitrine fixa e a janela ampliada (código e CSS) chegam sob demanda: o módulo só é carregado quando alguém precisa * dele. Aqui os módulos pesados são trocados por dublês que contam quantas vezes foram carregados, e o React.lazy é o * de verdade (os outros testes de showcase o trocam por componentes síncronos). A fábrica de cada dublê roda uma vez * por arquivo, na primeira importação: os testes seguem esta ordem (celular, janela ampliada, desktop) e a contagem * acumula de um para o outro. */ const mockLoads = { pinned: 0, lightbox: 0 }; jest.mock("./PinnedReel", () => { mockLoads.pinned += 1; return { __esModule: true, default: ({ items }: { items: unknown[] }) => (
{items.length} projetos fixos
), }; }); jest.mock("./ProjectLightbox", () => { mockLoads.lightbox += 1; return { ProjectLightbox: ({ index }: { index: number }) => (
aberto:{index}
), }; }); jest.mock("@/app/hooks/useReducedMotion", () => ({ useReducedMotion: () => false, })); const projects = getFeaturedProjects(null); const originalMatchMedia = window.matchMedia; function mockViewport({ phone }: { phone: boolean }) { window.matchMedia = jest.fn().mockImplementation((query: string) => ({ matches: (query === MOBILE_QUERY && phone) || (query === PINNED_QUERY && !phone), media: query, addEventListener() {}, removeEventListener() {}, })); } afterEach(() => { window.matchMedia = originalMatchMedia; }); function Opener() { const open = useShowcaseOpen(); return ( ); } describe("carregamento sob demanda", () => { it("1. on a phone nothing heavy is loaded: the list is all there is, and the window waits for a click", async () => { mockViewport({ phone: true }); render( , ); await act(async () => { await Promise.resolve(); }); expect(mockLoads).toEqual({ pinned: 0, lightbox: 0 }); expect(screen.queryByTestId("pinned-reel")).toBeNull(); expect( screen.getByRole("region", { name: "Projetos de clientes" }), ).toBeInTheDocument(); }); it("2. the enlarged window is loaded when a project is enlarged, and only then", async () => { mockViewport({ phone: true }); render( , ); expect(mockLoads.lightbox).toBe(0); fireEvent.click(screen.getByRole("button", { name: "ampliar" })); expect( await screen.findByRole("dialog", { name: "ampliado" }), ).toBeInTheDocument(); expect(mockLoads.lightbox).toBe(1); expect(mockLoads.pinned).toBe(0); }); it("3. the pinned reel is loaded where it fits, holding the place with a block of the same height until it arrives", async () => { mockViewport({ phone: false }); const { container } = render( , ); // enquanto o pacote chega: o bloco reservado (com a conta da altura), não uma página que encolhe const hold = container.querySelector(".ds-reel-placeholder"); expect(hold).not.toBeNull(); expect(hold).toHaveAttribute("aria-hidden", "true"); expect((hold as HTMLElement).style.getPropertyValue("--reel-sum")).not.toBe( "", ); expect(screen.queryByTestId("pinned-reel")).toBeNull(); expect(await screen.findByTestId("pinned-reel")).toBeInTheDocument(); expect(mockLoads.pinned).toBe(1); expect(container.querySelector(".ds-reel-placeholder")).toBeNull(); }); it("4. the page code never imports the heavy modules by name: only the *Lazy files do, with import()", () => { const fs = require("node:fs") as typeof import("node:fs"); const path = require("node:path") as typeof import("node:path"); const read = (file: string) => fs.readFileSync(path.join(__dirname, file), "utf8"); expect(read("ShowcaseProvider.tsx")).not.toMatch( /from "\.\/ProjectLightbox"/, ); expect(read("ProjectReel.tsx")).not.toMatch(/from "\.\/PinnedReel"/); expect(read("ProjectLightboxLazy.tsx")).toMatch( /import\("\.\/ProjectLightbox"\)/, ); expect(read("PinnedReelLazy.tsx")).toMatch(/import\("\.\/PinnedReel"\)/); // e o CSS de cada um viaja com o componente dele, não com a página expect(read("PinnedReel.tsx")).toMatch(/import "\.\/pinned-reel\.css"/); expect(read("ProjectLightbox.tsx")).toMatch(/import "\.\/lightbox\.css"/); // só as linhas de import contam: o comentário do layout pode citar os arquivos pelo nome expect( fs.readFileSync(path.join(__dirname, "../../layout.tsx"), "utf8"), ).not.toMatch( /^\s*import\s+["'][^"']*(pinned-reel|lightbox|reel-list)\.css["']/m, ); // a lista de projetos e o casco da vitrine (reel-list.css) vêm dentro do global: a home não carrega uma terceira // folha bloqueante, e nenhum componente a importa expect(read("ProjectReel.tsx")).not.toMatch(/reel-list\.css/); expect(read("ProjectList.tsx")).not.toMatch(/reel-list\.css/); expect( fs.readFileSync(path.join(__dirname, "../../globals.css"), "utf8"), ).toMatch(/@import "\.\/components\/showcase\/reel-list\.css"/); }); });