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);
});
});