Files
portifolio-evandrodevbr/app/components/ProjectVideo.test.tsx
T
Evandro Fonseca Junior c9a0b80656 perf(movimento): uma transição, 3D leve e vídeo certo por aparelho
Passagens com fade e 32 px, cena anterior termina em opacidade 0, sem visibility nem clip-path. 3D do hero como fundo ambiente, sob demanda e desligado em toque, aparelho fraco, saveData e movimento reduzido. Lenis fora do toque e salto direto em âncoras distantes. Vitrine só de clientes, até 3 capítulos, fixa só no desktop e carrossel no celular; vídeo só do projeto em cena com preload metadata. Rótulos mono a partir de 12 px e limão só no contato.
2026-10-14 03:32:23 -03:00

146 lines
5.1 KiB
TypeScript

import { act, render, screen } from "@testing-library/react";
import { getShowcaseAssets } from "@/lib/project-showcase";
import { ProjectVideo } from "./ProjectVideo";
let reducedMotion = false;
jest.mock("@/app/hooks/useReducedMotion", () => ({
useReducedMotion: () => reducedMotion,
}));
const video = getShowcaseAssets("nutriscan", "desktop");
beforeEach(() => {
reducedMotion = false;
jest.spyOn(HTMLMediaElement.prototype, "play").mockResolvedValue();
jest.spyOn(HTMLMediaElement.prototype, "pause").mockImplementation(() => {});
jest.spyOn(HTMLMediaElement.prototype, "load").mockImplementation(() => {});
});
afterEach(() => {
jest.restoreAllMocks();
Object.defineProperty(navigator, "connection", {
configurable: true,
value: undefined,
});
});
describe("ProjectVideo", () => {
it("renders a muted looping inline video that downloads nothing until it is active", () => {
const { container } = render(
<ProjectVideo video={video} title="Demo" active={false} />,
);
const element = container.querySelector("video");
expect(element).not.toBeNull();
expect(element).toHaveAttribute("autoplay");
expect(element).toHaveAttribute("loop");
expect(element).toHaveAttribute("playsinline");
expect(element).toHaveAttribute("preload", "metadata");
expect(element).toHaveAttribute("poster", video.poster);
expect(element?.muted).toBe(true);
expect(element).toHaveAttribute("data-ready", "false");
expect(container.querySelectorAll("source")).toHaveLength(0);
expect(HTMLMediaElement.prototype.play).not.toHaveBeenCalled();
});
it("attaches the sources and plays when it becomes the active card, then pauses when it stops being", () => {
const { container, rerender } = render(
<ProjectVideo video={video} title="Demo" active={false} />,
);
rerender(<ProjectVideo video={video} title="Demo" active />);
const sources = [...container.querySelectorAll("source")];
expect(sources.map((source) => source.getAttribute("src"))).toEqual(
video.sources.map((source) => source.src),
);
expect(HTMLMediaElement.prototype.load).toHaveBeenCalled();
expect(HTMLMediaElement.prototype.play).toHaveBeenCalled();
rerender(<ProjectVideo video={video} title="Demo" active={false} />);
expect(HTMLMediaElement.prototype.pause).toHaveBeenCalled();
// sai de cena, mas o arquivo já baixado fica: voltar a tocar não baixa de novo
expect(container.querySelectorAll("source")).toHaveLength(2);
});
it("fades the video in once it is playing", () => {
const { container } = render(
<ProjectVideo video={video} title="Demo" active />,
);
const element = container.querySelector("video") as HTMLVideoElement;
act(() => {
element.dispatchEvent(new Event("playing"));
});
expect(element).toHaveAttribute("data-ready", "true");
});
it("does not even request the poster while the card is far from the screen", () => {
const { container, rerender } = render(
<ProjectVideo
video={video}
title="Demo"
active={false}
showPoster={false}
/>,
);
expect(container.querySelector("video")).not.toHaveAttribute("poster");
rerender(
<ProjectVideo video={video} title="Demo" active={false} showPoster />,
);
expect(container.querySelector("video")).toHaveAttribute(
"poster",
video.poster,
);
});
it("swaps to the sources of the new device when the version changes", () => {
const mobile = getShowcaseAssets("nutriscan", "mobile");
const { container, rerender } = render(
<ProjectVideo video={video} title="Demo" active />,
);
const loads = jest.mocked(HTMLMediaElement.prototype.load).mock.calls
.length;
rerender(<ProjectVideo video={mobile} title="Demo" active />);
expect(
[...container.querySelectorAll("source")].map((source) =>
source.getAttribute("src"),
),
).toEqual(mobile.sources.map((source) => source.src));
expect(
jest.mocked(HTMLMediaElement.prototype.load).mock.calls.length,
).toBeGreaterThan(loads);
});
it("shows only the poster, with no video element, when the data saver is on", () => {
Object.defineProperty(navigator, "connection", {
configurable: true,
value: { saveData: true },
});
const { container } = render(
<ProjectVideo video={video} title="Demo" active />,
);
expect(container.querySelector("video")).toBeNull();
expect(container.querySelector("source")).toBeNull();
expect(
screen.getByRole("img", { name: "Captura de tela de Demo" }),
).toBeInTheDocument();
expect(HTMLMediaElement.prototype.play).not.toHaveBeenCalled();
});
it("shows only the poster, with no video element, when the user prefers reduced motion", () => {
reducedMotion = true;
const { container } = render(
<ProjectVideo video={video} title="Demo" active />,
);
expect(container.querySelector("video")).toBeNull();
expect(
screen.getByRole("img", { name: "Captura de tela de Demo" }),
).toBeInTheDocument();
expect(HTMLMediaElement.prototype.play).not.toHaveBeenCalled();
});
});