Files
Evandro Fonseca Junior 6ac87824f3 perf(movimento): sem tempo morto, vídeos regravados e 3D sem React Three Fiber
Legenda do mergulho até Projetos cobrir a cena; transição de página sem blur e com fundo escuro; foco no conteúdo ao trocar de página; listas com marcador e dois pesos de título; vídeos da SEV e da SC Plus regravados sem quadros brancos; scrub a 12-15 fps (-30%); vitrine com uma parada de Tab e setas, contador no celular e CSS sob demanda; âncoras num módulo só (fragment-nav); FramePulse com setInterval; CSS morto removido.
2026-10-14 17:01:37 -03:00

142 lines
4.0 KiB
TypeScript

import { startFramePulse } from "./FramePulse";
const invalidate = jest.fn();
let stops: Array<() => void> = [];
/** Liga a batida e guarda o desligamento para o afterEach não deixar ouvinte para trás. */
function start(options?: Parameters<typeof startFramePulse>[1]) {
const stop = startFramePulse(invalidate, options);
stops.push(stop);
return stop;
}
beforeEach(() => {
jest.useFakeTimers();
invalidate.mockClear();
stops = [];
});
afterEach(() => {
for (const stop of stops) stop();
jest.useRealTimers();
});
const advance = (ms: number) => jest.advanceTimersByTime(ms);
describe("startFramePulse", () => {
it("asks for frames at about 30 per second, not 60", () => {
start();
advance(1000);
expect(invalidate.mock.calls.length).toBeGreaterThan(20);
expect(invalidate.mock.calls.length).toBeLessThanOrEqual(34);
});
it("asks for the first frame right away", () => {
start();
expect(invalidate).toHaveBeenCalledTimes(1);
});
it("is a single setInterval, never a second animation-frame loop", () => {
const frames = jest.spyOn(window, "requestAnimationFrame");
start();
expect(jest.getTimerCount()).toBe(1);
advance(1000);
expect(frames).not.toHaveBeenCalled();
frames.mockRestore();
});
it("does nothing once it is stopped (the hero is off screen)", () => {
const stop = start();
advance(200);
const running = invalidate.mock.calls.length;
expect(running).toBeGreaterThan(0);
stop();
advance(1000);
expect(invalidate.mock.calls.length).toBe(running);
expect(jest.getTimerCount()).toBe(0);
});
it("removes its listeners when it stops", () => {
const stop = start();
stop();
invalidate.mockClear();
window.dispatchEvent(new Event("pointermove"));
advance(500);
expect(invalidate).not.toHaveBeenCalled();
expect(jest.getTimerCount()).toBe(0);
});
it("can be stopped twice", () => {
const stop = start();
stop();
expect(() => stop()).not.toThrow();
});
});
describe("startFramePulse: stops when nobody is looking", () => {
it("stops drawing, and has no timer left, after 5 s without a cursor or scroll", () => {
start();
advance(4000);
const drawn = invalidate.mock.calls.length;
expect(drawn).toBeGreaterThan(80); // ainda batendo, ~30 por segundo
advance(1500); // passou dos 5 s
const stopped = invalidate.mock.calls.length;
// parou perto dos 5 s: no máximo 5 s de batidas a ~30 por segundo
expect(stopped).toBeLessThanOrEqual(5 * 32);
advance(3000);
expect(invalidate.mock.calls.length).toBe(stopped);
expect(jest.getTimerCount()).toBe(0);
});
it.each([["pointermove"], ["scroll"], ["wheel"], ["keydown"]])(
"wakes up again with %s and keeps drawing",
(type) => {
start();
advance(7000);
const stopped = invalidate.mock.calls.length;
window.dispatchEvent(new Event(type));
advance(500);
expect(invalidate.mock.calls.length).toBeGreaterThan(stopped + 8);
},
);
it("wakes up once, not once per gesture (one timer, however many events arrive)", () => {
start();
for (let i = 0; i < 20; i++) window.dispatchEvent(new Event("pointermove"));
expect(jest.getTimerCount()).toBe(1);
});
it("each gesture buys another 5 s: it does not stop while there is movement", () => {
start();
for (let i = 0; i < 4; i++) {
advance(3000);
window.dispatchEvent(new Event("pointermove"));
}
const before = invalidate.mock.calls.length;
advance(1000);
expect(invalidate.mock.calls.length).toBeGreaterThan(before + 20);
});
it("lets the idle time and the rate be configured", () => {
start({ idleMs: 1000 });
advance(1500);
const stopped = invalidate.mock.calls.length;
advance(2000);
expect(invalidate.mock.calls.length).toBe(stopped);
invalidate.mockClear();
start({ fps: 10 });
advance(1000);
expect(invalidate.mock.calls.length).toBeLessThanOrEqual(11);
expect(invalidate.mock.calls.length).toBeGreaterThanOrEqual(9);
});
});