import { act, fireEvent, render, screen, within } from "@testing-library/react"; import { renderToString } from "react-dom/server"; import { MOBILE_QUERY } from "@/lib/device"; import { getFeaturedProjects } from "@/lib/portfolio-data"; import { getShowcaseConfig, videoFractionAtScroll, } from "@/lib/project-showcase"; import { ProjectReel } from "./ProjectReel"; import { ShowcaseProvider } from "./ShowcaseProvider"; import { CAPTION_OUT_MS, reelOffset, videoProgress } from "./showcase-math"; import { PINNED_QUERY } from "./useShowcaseMedia"; // o pacote da vitrine fixa e o da janela ampliada chegam sob demanda (React.lazy): aqui entram já, para o teste não esperar jest.mock("./PinnedReelLazy", () => ({ PinnedReelLazy: jest.requireActual("./PinnedReel").default, })); jest.mock("./ProjectLightboxLazy", () => ({ ProjectLightboxLazy: jest.requireActual("./ProjectLightbox").ProjectLightbox, })); let reducedMotion = false; jest.mock("@/app/hooks/useReducedMotion", () => ({ useReducedMotion: () => reducedMotion, })); // o dublê do põe as props num
para o teste ler jest.mock("react", () => jest.requireActual("@/__mocks__/view-transition-probe").reactWithProbe(), ); const PIN = 800; const CONTAINER = 7000; const SCROLLABLE = CONTAINER - PIN; const HEADER = 60; let reelTop = HEADER; /** largura do celular da moldura: 0 simula uma moldura escondida por CSS */ let phoneWidth = 90; const originalMatchMedia = window.matchMedia; /** Tela de desktop por padrão (cabe a vitrine fixa); `phone` simula um celular. */ function mockViewport({ phone = false }: { phone?: boolean } = {}) { window.matchMedia = jest.fn().mockImplementation((query: string) => ({ matches: (query === MOBILE_QUERY && phone) || (query === PINNED_QUERY && !phone), media: query, addEventListener() {}, removeEventListener() {}, })); } /** O arquivo do pôster que a imagem dentro de `element` pede (decodificado); "" sem imagem. */ const posterIn = (element: Element | null | undefined) => decodeURIComponent(element?.querySelector("img")?.getAttribute("src") ?? ""); const projects = getFeaturedProjects(null); const weights = [ "sev-modaintima", "lavacao-047", "nutriscan", "sc-plus", "as-servicos", "julia-beauty", ].map((id) => getShowcaseConfig(id)?.weight ?? 1); const originalRect = Element.prototype.getBoundingClientRect; const originalOffsetHeight = Object.getOwnPropertyDescriptor( HTMLElement.prototype, "offsetHeight", ); const originalOffsetWidth = Object.getOwnPropertyDescriptor( HTMLElement.prototype, "offsetWidth", ); beforeAll(() => { Object.defineProperty(HTMLElement.prototype, "offsetHeight", { configurable: true, get(this: HTMLElement) { if (this.classList.contains("ds-reel")) return CONTAINER; if (this.classList.contains("ds-reel-pin")) return PIN; return 0; }, }); Object.defineProperty(HTMLElement.prototype, "offsetWidth", { configurable: true, get(this: HTMLElement) { return this.classList.contains("ds-reel-phone") ? phoneWidth : 0; }, }); // jsdom ainda não implementa o modal HTMLDialogElement.prototype.showModal = function showModal( this: HTMLDialogElement, ) { this.setAttribute("open", ""); }; HTMLDialogElement.prototype.close = function close(this: HTMLDialogElement) { this.removeAttribute("open"); this.dispatchEvent(new Event("close")); }; Element.prototype.getBoundingClientRect = function rect(this: Element) { if (this.classList.contains("ds-reel")) return { top: reelTop, bottom: reelTop + CONTAINER, height: CONTAINER, left: 0, right: 1000, width: 1000, x: 0, y: reelTop, } as DOMRect; return originalRect.call(this); }; window.scrollTo = jest.fn(); }); afterAll(() => { Element.prototype.getBoundingClientRect = originalRect; if (originalOffsetHeight) Object.defineProperty( HTMLElement.prototype, "offsetHeight", originalOffsetHeight, ); if (originalOffsetWidth) Object.defineProperty( HTMLElement.prototype, "offsetWidth", originalOffsetWidth, ); }); beforeEach(() => { jest.useFakeTimers(); reducedMotion = false; reelTop = HEADER; phoneWidth = 90; mockViewport(); (window.scrollTo as jest.Mock).mockClear(); jest.spyOn(HTMLMediaElement.prototype, "play").mockResolvedValue(); jest.spyOn(HTMLMediaElement.prototype, "pause").mockImplementation(() => {}); }); afterEach(() => { jest.useRealTimers(); jest.restoreAllMocks(); window.matchMedia = originalMatchMedia; Object.defineProperty(navigator, "connection", { configurable: true, value: undefined, }); }); function renderReel() { const view = render( , ); act(() => { jest.advanceTimersByTime(50); }); return view; } /** jsdom não carrega mídia: cada