Files
portifolio-evandrodevbr/app/components/showcase/lazy-loading.test.tsx
T
Evandro Fonseca Junior 204ebd5fa5 fix(navegacao): foco em âncora de outra página, vitrine acessível e 3D mais leve
Foco cai no destino ao chegar de outra página; âncoras ignoram ?tipo=; movimento reduzido salta em todos os cliques; cronômetro da janela ampliada; degradê no topo de Projetos e legendas do mergulho com intervalos testados; vitrine acima da barra de cookies e com 2 paradas por projeto; reel-list.css no global; 3D montado em tarefas curtas, limitado a ~30 qps e estático com WebGL por software; pôsteres da vitrine de 330 para 158 KB; SC Plus regravado; scripts sem funções acima de complexidade 10.
2026-10-14 19:14:48 -03:00

160 lines
5.7 KiB
TypeScript

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[] }) => (
<div data-testid="pinned-reel">{items.length} projetos fixos</div>
),
};
});
jest.mock("./ProjectLightbox", () => {
mockLoads.lightbox += 1;
return {
ProjectLightbox: ({ index }: { index: number }) => (
<div role="dialog" aria-label="ampliado">
aberto:{index}
</div>
),
};
});
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 (
<button type="button" onClick={() => open("sev-modaintima")}>
ampliar
</button>
);
}
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(
<ShowcaseProvider projects={projects}>
<ProjectReel />
</ShowcaseProvider>,
);
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(
<ShowcaseProvider projects={projects}>
<Opener />
</ShowcaseProvider>,
);
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(
<ShowcaseProvider projects={projects}>
<ProjectReel />
</ShowcaseProvider>,
);
// 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"/);
});
});