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.
160 lines
5.7 KiB
TypeScript
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"/);
|
|
});
|
|
});
|