import { act, render } from "@testing-library/react"; import FilmGrain from "./FilmGrain"; /* IntersectionObserver controlado: o teste decide quando cada tela de projeto entra ou sai da janela. */ type Observer = { callback: IntersectionObserverCallback; observed: Set; disconnect: jest.Mock; }; let observers: Observer[] = []; const original = global.IntersectionObserver; beforeEach(() => { observers = []; global.IntersectionObserver = class { private readonly record: Observer; constructor(callback: IntersectionObserverCallback) { this.record = { callback, observed: new Set(), disconnect: jest.fn() }; observers.push(this.record); } observe(target: Element) { this.record.observed.add(target); } unobserve(target: Element) { this.record.observed.delete(target); } disconnect() { this.record.disconnect(); } takeRecords() { return []; } } as unknown as typeof IntersectionObserver; }); afterEach(() => { global.IntersectionObserver = original; document.body.innerHTML = ""; }); function addScreen(className: string) { const element = document.createElement("div"); element.className = className; document.body.append(element); return element; } /** O navegador avisa que `target` entrou (ou saiu) da janela; `height` 0 é uma tela escondida por CSS. */ function intersect(target: Element, isIntersecting: boolean, height = 600) { act(() => { for (const observer of observers) if (observer.observed.has(target)) observer.callback( [ { target, isIntersecting, boundingClientRect: { height }, } as unknown as IntersectionObserverEntry, ], {} as IntersectionObserver, ); }); } describe("static cinematic effects", () => { it("renders grain without a canvas", () => { const grain = render(); expect(grain.container.firstElementChild).toHaveAttribute( "aria-hidden", "true", ); expect(grain.container.querySelector("canvas")).not.toBeInTheDocument(); }); }); describe("film grain over the project screens", () => { it("steps aside while a project screen is in view and comes back when it leaves", () => { const reel = addScreen("ds-reel"); const { container } = render(); const grain = container.firstElementChild as HTMLElement; expect(grain).not.toHaveAttribute("data-over-projects"); intersect(reel, true); // a vitrine entrou na janela expect(grain).toHaveAttribute("data-over-projects"); intersect(reel, false); // passou expect(grain).not.toHaveAttribute("data-over-projects"); }); it("also covers the browser and phone frames and the card screens", () => { for (const className of ["ds-bframe", "ds-phone", "ds-frame"]) { const screen = addScreen(className); const { container, unmount } = render(); intersect(screen, true); expect(container.firstElementChild).toHaveAttribute("data-over-projects"); unmount(); document.body.innerHTML = ""; } }); it("stays on when there is no project screen on the page", () => { const { container } = render(); expect(observers[0].observed.size).toBe(0); expect(container.firstElementChild).not.toHaveAttribute( "data-over-projects", ); }); });