import { render, screen } from "@testing-library/react"; import { getShowcaseAssets } from "@/lib/project-showcase"; import { ProjectVideo } from "./ProjectVideo"; /** * Testes de caminhos-base de ProjectVideo (complemento de ProjectVideo.test.tsx): modo só pôster * (movimento reduzido ou "economizar dados") com e sem pôster pedido, inativo x ativo, vídeo sem fontes, * falha do play() e a combinação de ativo com modo só pôster. */ let reducedMotion = false; jest.mock("@/app/hooks/useReducedMotion", () => ({ useReducedMotion: () => reducedMotion, })); const video = getShowcaseAssets("nutriscan", "desktop"); function setSaveData(value: boolean | undefined) { Object.defineProperty(navigator, "connection", { configurable: true, value: value === undefined ? undefined : { saveData: value }, }); } beforeEach(() => { reducedMotion = false; setSaveData(undefined); jest.spyOn(HTMLMediaElement.prototype, "play").mockResolvedValue(); jest.spyOn(HTMLMediaElement.prototype, "pause").mockImplementation(() => {}); jest.spyOn(HTMLMediaElement.prototype, "load").mockImplementation(() => {}); }); afterEach(() => { jest.restoreAllMocks(); setSaveData(undefined); }); describe("ProjectVideo: modo só pôster", () => { it.each([ [ "reduced motion", () => { reducedMotion = true; }, ], ["the data saver", () => setSaveData(true)], ])( "path: %s and the poster is not wanted yet -> renders nothing at all", (_l, arrange) => { arrange(); const { container } = render( , ); expect(container).toBeEmptyDOMElement(); }, ); it.each([ [ "reduced motion", () => { reducedMotion = true; }, ], ["the data saver", () => setSaveData(true)], ])( "path: %s and the poster is wanted -> only the image, with the project name in its alt", (_l, arrange) => { arrange(); const { container } = render( , ); expect(container.querySelector("video")).toBeNull(); const image = screen.getByRole("img", { name: "Captura de tela de Demo", }); expect(image).toHaveAttribute("width", String(video.width)); expect(image).toHaveAttribute("height", String(video.height)); }, ); it("path: data saver off and motion allowed -> the video element, never the image", () => { setSaveData(false); const { container } = render( , ); expect(container.querySelector("video")).not.toBeNull(); expect(screen.queryByRole("img")).toBeNull(); }); it("path: active card but poster-only mode -> nothing is attached and nothing plays", () => { reducedMotion = true; render(); expect(HTMLMediaElement.prototype.play).not.toHaveBeenCalled(); expect(HTMLMediaElement.prototype.load).not.toHaveBeenCalled(); }); }); describe("ProjectVideo: vídeo", () => { it("path: far card (poster not wanted) -> the video has no poster attribute", () => { const { container } = render( , ); expect(container.querySelector("video")).not.toHaveAttribute("poster"); }); it("path: inactive and never activated -> no source, no load, no play, no pause", () => { render(); expect(HTMLMediaElement.prototype.load).not.toHaveBeenCalled(); expect(HTMLMediaElement.prototype.play).not.toHaveBeenCalled(); expect(HTMLMediaElement.prototype.pause).not.toHaveBeenCalled(); }); it("path: becomes active -> muted is forced on the element before play()", () => { const { container, rerender } = render( , ); const element = container.querySelector("video") as HTMLVideoElement; element.muted = false; let mutedAtPlay: boolean | undefined; jest.mocked(HTMLMediaElement.prototype.play).mockImplementation(function ( this: HTMLMediaElement, ) { mutedAtPlay = this.muted; return Promise.resolve(); }); rerender(); expect(mutedAtPlay).toBe(true); }); it("path: play() is rejected by the browser -> swallowed, the video stays mounted", async () => { jest .mocked(HTMLMediaElement.prototype.play) .mockRejectedValue(new DOMException("blocked", "NotAllowedError")); const { container } = render( , ); await Promise.resolve(); expect(container.querySelector("video")).not.toBeNull(); }); it("path: no sources in the assets -> becomes active without calling load()", () => { const empty = { ...video, sources: [] }; const { container } = render( , ); expect(container.querySelectorAll("source")).toHaveLength(0); expect(HTMLMediaElement.prototype.load).not.toHaveBeenCalled(); expect(HTMLMediaElement.prototype.play).toHaveBeenCalled(); }); it("path: active -> inactive -> active again -> plays again without reloading the sources", () => { const { container, rerender } = render( , ); const loadsAfterFirst = jest.mocked(HTMLMediaElement.prototype.load).mock .calls.length; const playsAfterFirst = jest.mocked(HTMLMediaElement.prototype.play).mock .calls.length; rerender(); rerender(); expect(jest.mocked(HTMLMediaElement.prototype.load).mock.calls.length).toBe( loadsAfterFirst, ); expect(jest.mocked(HTMLMediaElement.prototype.play).mock.calls.length).toBe( playsAfterFirst + 1, ); expect(container.querySelectorAll("source")).toHaveLength(2); }); });