Files
portifolio-evandrodevbr/app/components/story/ScrollStory.test.tsx
T

81 lines
2.9 KiB
TypeScript

import { render, screen, waitFor } from "@testing-library/react";
import ScrollStory from "./ScrollStory";
// gsap é carregado via import() dinâmico dentro do effect; o contrato testado
// aqui é markup do reel (cenas/HUD/alvos), não a timeline — que é verificada
// no browser via probe CDP. Mock leve mantém a suíte em jsdom.
jest.mock("gsap", () => {
const chain = { set: jest.fn(), fromTo: jest.fn(), to: jest.fn() };
Object.assign(chain, {
set: jest.fn(() => chain),
fromTo: jest.fn(() => chain),
to: jest.fn(() => chain),
});
return {
gsap: {
registerPlugin: jest.fn(),
set: jest.fn(),
timeline: jest.fn(() => chain),
ticker: { add: jest.fn(), remove: jest.fn(), lagSmoothing: jest.fn() },
utils: { toArray: jest.fn(() => []) },
context: jest.fn((fn: () => void) => {
fn();
return { revert: jest.fn() };
}),
},
};
});
jest.mock("gsap/ScrollTrigger", () => ({
ScrollTrigger: { getAll: jest.fn(() => []) },
}));
const props = { projectsCount: 5, experiencesCount: 6, skillsCount: 18 };
function mockMatchMedia(matches: boolean) {
Object.defineProperty(window, "matchMedia", {
writable: true,
value: jest.fn().mockImplementation((query: string) => ({
matches,
media: query,
onchange: null,
addListener: jest.fn(),
removeListener: jest.fn(),
addEventListener: jest.fn(),
removeEventListener: jest.fn(),
dispatchEvent: jest.fn(),
})),
});
}
describe("ScrollStory", () => {
afterEach(() => {
jest.restoreAllMocks();
mockMatchMedia(false);
});
it("renderiza fallback empilhado com conteúdo real com reduced motion", () => {
mockMatchMedia(true);
const { container } = render(<ScrollStory {...props} />);
// sem palco scrub: capítulos legíveis no fluxo, números reais do portfólio
expect(container.querySelector("[data-story]")).not.toBeInTheDocument();
expect(container).toHaveTextContent("01 — Visão geral");
expect(container).toHaveTextContent("04 — Próximo ciclo");
expect(container).toHaveTextContent("5");
expect(screen.getAllByText(/sistemas ativos no portfólio/)).toHaveLength(1);
});
it("renderiza o reel com 4 cenas, HUD e índice quando o engine liga", async () => {
const { container } = render(<ScrollStory {...props} />);
await waitFor(
() => expect(container.querySelector("[data-story]")).toBeInTheDocument(),
{ timeout: 10000 },
);
expect(container.querySelectorAll("[data-story-scene]")).toHaveLength(4);
expect(container.querySelector("[data-story-index]")).toHaveTextContent("01");
expect(container.querySelector("[data-story-bar]")).toBeInTheDocument();
// count-up é alimentado pela timeline: markup carrega o alvo real
const num = container.querySelector("[data-story-num]");
expect(num?.getAttribute("data-story-num-to")).toBe("5");
}, 20000);
});