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.
51 lines
1.4 KiB
TypeScript
51 lines
1.4 KiB
TypeScript
import { act, render } from "@testing-library/react";
|
|
import FramePulse from "./FramePulse";
|
|
|
|
const mockInvalidate = jest.fn();
|
|
|
|
jest.mock("@react-three/fiber", () => ({
|
|
useThree: (select: (state: { invalidate: () => void }) => unknown) =>
|
|
select({ invalidate: mockInvalidate }),
|
|
}));
|
|
|
|
beforeEach(() => {
|
|
jest.useFakeTimers();
|
|
mockInvalidate.mockClear();
|
|
});
|
|
|
|
afterEach(() => {
|
|
jest.useRealTimers();
|
|
});
|
|
|
|
describe("FramePulse", () => {
|
|
it("asks for frames at about 30 per second while active, not 60", () => {
|
|
render(<FramePulse active />);
|
|
act(() => {
|
|
jest.advanceTimersByTime(1000);
|
|
});
|
|
expect(mockInvalidate.mock.calls.length).toBeGreaterThan(20);
|
|
expect(mockInvalidate.mock.calls.length).toBeLessThanOrEqual(34);
|
|
});
|
|
|
|
it("draws nothing at all when it is not active (off screen)", () => {
|
|
const { rerender } = render(<FramePulse active={false} />);
|
|
act(() => {
|
|
jest.advanceTimersByTime(1000);
|
|
});
|
|
expect(mockInvalidate).not.toHaveBeenCalled();
|
|
|
|
rerender(<FramePulse active />);
|
|
act(() => {
|
|
jest.advanceTimersByTime(200);
|
|
});
|
|
const running = mockInvalidate.mock.calls.length;
|
|
expect(running).toBeGreaterThan(0);
|
|
|
|
rerender(<FramePulse active={false} />);
|
|
act(() => {
|
|
jest.advanceTimersByTime(1000);
|
|
});
|
|
expect(mockInvalidate.mock.calls.length).toBe(running);
|
|
});
|
|
});
|