import { act, fireEvent, render, screen } from "@testing-library/react"; import { getFeaturedProjects } from "@/lib/portfolio-data"; import { ProjectMedia } from "./ProjectMedia"; import { ShowcaseProvider } from "./ShowcaseProvider"; let reducedMotion = false; jest.mock("@/app/hooks/useReducedMotion", () => ({ useReducedMotion: () => reducedMotion, })); jest.mock("./ProjectLightbox", () => ({ ProjectLightbox: () =>
, })); const projects = getFeaturedProjects(null); const nutriscan = projects.find((project) => project.id === "nutriscan"); if (!nutriscan) throw new Error("projeto ausente"); type Callback = IntersectionObserverCallback; const callbacks: Callback[] = []; const Original = global.IntersectionObserver; beforeEach(() => { jest.useFakeTimers(); reducedMotion = false; callbacks.length = 0; global.IntersectionObserver = class { constructor(callback: Callback) { callbacks.push(callback); } observe() {} unobserve() {} disconnect() {} takeRecords() { return []; } } as unknown as typeof IntersectionObserver; jest.spyOn(HTMLMediaElement.prototype, "play").mockResolvedValue(); jest.spyOn(HTMLMediaElement.prototype, "pause").mockImplementation(() => {}); jest.spyOn(HTMLMediaElement.prototype, "load").mockImplementation(() => {}); }); afterEach(() => { jest.useRealTimers(); jest.restoreAllMocks(); global.IntersectionObserver = Original; }); function renderCard() { const view = render(
} />
, ); const frame = view.container.querySelector(".ds-frame") as HTMLElement; frame.getBoundingClientRect = () => ({ top: 300, height: 300, bottom: 600, left: 0, right: 400, width: 400, x: 0, y: 300, }) as DOMRect; return { ...view, frame }; } function show(frame: HTMLElement) { act(() => { for (const callback of callbacks) callback( [ { target: frame, intersectionRatio: 1, isIntersecting: true, } as unknown as IntersectionObserverEntry, ], {} as IntersectionObserver, ); jest.advanceTimersByTime(40); }); } describe("ProjectMedia", () => { it("frames the video with the project's address and a button that enlarges it", () => { renderCard(); expect(screen.getByText("nutriscan.evandro.dev.br")).toBeInTheDocument(); const expand = screen.getByRole("button", { name: "Ampliar o vídeo de NutriScan", }); expect(expand).toHaveAttribute("aria-haspopup", "dialog"); fireEvent.click(expand); expect( screen.getByRole("dialog", { name: "ampliado" }), ).toBeInTheDocument(); }); it("plays only while the card is the active one", () => { const { frame, container } = renderCard(); const video = container.querySelector("video") as HTMLVideoElement; expect(container.querySelector(".ds-frame")).toHaveAttribute( "data-active", "false", ); expect(video.querySelectorAll("source")).toHaveLength(0); show(frame); expect(container.querySelector(".ds-frame")).toHaveAttribute( "data-active", "true", ); expect(video.querySelectorAll("source")).toHaveLength(2); }); it("lets the visitor pause the videos that play on their own, and resume them", () => { const { frame, container } = renderCard(); show(frame); expect(container.querySelector(".ds-frame")).toHaveAttribute( "data-active", "true", ); fireEvent.click( screen.getByRole("button", { name: "Pausar os vídeos dos projetos" }), ); act(() => { jest.advanceTimersByTime(40); }); expect(container.querySelector(".ds-frame")).toHaveAttribute( "data-active", "false", ); fireEvent.click( screen.getByRole("button", { name: "Retomar os vídeos dos projetos" }), ); act(() => { jest.advanceTimersByTime(40); }); expect(container.querySelector(".ds-frame")).toHaveAttribute( "data-active", "true", ); }); it("has no pause button when nothing plays on its own (reduced motion)", () => { reducedMotion = true; renderCard(); expect( screen.queryByRole("button", { name: /vídeos dos projetos/ }), ).toBeNull(); }); it("renders nothing for a project without a recorded video", () => { const { container } = render( , ); expect(container.querySelector(".ds-frame")).toBeNull(); }); it("does nothing, without crashing, when used outside the provider", () => { const { container } = render( } />, ); fireEvent.click( screen.getByRole("button", { name: "Ampliar o vídeo de NutriScan" }), ); expect(container.querySelector(".ds-frame")).not.toBeNull(); }); });