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.
This commit is contained in:
Evandro Fonseca Junior committed 2026-10-14 03:32:23 -03:00
1 parent 8d07b8c6bb
commit c9a0b80656
48 files changed
+7210 -1241

No files matched your search

@@ -0,0 +1,297 @@
import { act, render, renderHook } from "@testing-library/react";
import { type RefObject, useRef } from "react";
import { renderToString } from "react-dom/server";
import { MOBILE_QUERY } from "@/lib/device";
import {
NEAR_MARGIN,
PINNED_QUERY,
useNearViewport,
usePinnedViewport,
useSaveData,
useViewportDevice,
} from "./useShowcaseMedia";
/**
* Testes de caminhos-base dos hooks de mídia da vitrine: aparelho (celular x desktop x sem matchMedia),
* vitrine fixa cabe ou não, "economizar dados" e a proximidade com a janela (com e sem IntersectionObserver).
*/
const originalMatchMedia = window.matchMedia;
const OriginalObserver = global.IntersectionObserver;
type MediaListener = () => void;
type FakeQuery = {
query: string;
matches: boolean;
listeners: Set<MediaListener>;
};
let queries: FakeQuery[] = [];
/** matchMedia falso: `matching` diz quais consultas valem; cada chamada devolve um objeto que guarda os ouvintes. */
function mockMedia(matching: string[]) {
queries = [];
window.matchMedia = jest.fn().mockImplementation((query: string) => {
const fake: FakeQuery = {
query,
matches: matching.includes(query),
listeners: new Set(),
};
queries.push(fake);
return {
get matches() {
return fake.matches;
},
media: query,
addEventListener: (_type: string, listener: MediaListener) =>
fake.listeners.add(listener),
removeEventListener: (_type: string, listener: MediaListener) =>
fake.listeners.delete(listener),
};
});
}
function mockNavigator(values: Record<string, unknown>) {
for (const [key, value] of Object.entries(values))
Object.defineProperty(navigator, key, { configurable: true, value });
}
afterEach(() => {
window.matchMedia = originalMatchMedia;
global.IntersectionObserver = OriginalObserver;
mockNavigator({ connection: undefined });
});
describe("useViewportDevice", () => {
it("path: server render (no effect yet) -> null, so no media file is chosen", () => {
function Probe() {
return <span>{String(useViewportDevice())}</span>;
}
expect(renderToString(<Probe />)).toContain("null");
});
it("path: browser without matchMedia -> desktop", () => {
// @ts-expect-error simula um navegador sem matchMedia
window.matchMedia = undefined;
const { result } = renderHook(() => useViewportDevice());
expect(result.current).toBe("desktop");
});
it("path: phone width matches -> mobile", () => {
mockMedia([MOBILE_QUERY]);
const { result } = renderHook(() => useViewportDevice());
expect(result.current).toBe("mobile");
expect(queries.map((q) => q.query)).toEqual([MOBILE_QUERY]);
});
it("path: phone width does not match -> desktop", () => {
mockMedia([]);
const { result } = renderHook(() => useViewportDevice());
expect(result.current).toBe("desktop");
});
it("path: the window is resized across the breakpoint -> follows both ways", () => {
mockMedia([]);
const { result } = renderHook(() => useViewportDevice());
expect(result.current).toBe("desktop");
act(() => {
queries[0].matches = true;
for (const listener of queries[0].listeners) listener();
});
expect(result.current).toBe("mobile");
act(() => {
queries[0].matches = false;
for (const listener of queries[0].listeners) listener();
});
expect(result.current).toBe("desktop");
});
it("path: unmount -> the media listener is removed", () => {
mockMedia([]);
const { unmount } = renderHook(() => useViewportDevice());
expect(queries[0].listeners.size).toBe(1);
unmount();
expect(queries[0].listeners.size).toBe(0);
});
});
describe("usePinnedViewport", () => {
it("path: server render -> null until the browser mounts", () => {
function Probe() {
return <span>{String(usePinnedViewport())}</span>;
}
expect(renderToString(<Probe />)).toContain("null");
});
it("path: browser without matchMedia -> the pinned reel is allowed", () => {
// @ts-expect-error simula um navegador sem matchMedia
window.matchMedia = undefined;
const { result } = renderHook(() => usePinnedViewport());
expect(result.current).toBe(true);
});
it("path: the screen is wide and tall enough -> true, using the shared query", () => {
mockMedia([PINNED_QUERY]);
const { result } = renderHook(() => usePinnedViewport());
expect(result.current).toBe(true);
expect(queries.map((q) => q.query)).toEqual([PINNED_QUERY]);
expect(PINNED_QUERY).toBe("(min-width: 768px) and (min-height: 561px)");
});
it("path: the screen is too small (phone or short window) -> false", () => {
mockMedia([]);
const { result } = renderHook(() => usePinnedViewport());
expect(result.current).toBe(false);
});
it("path: the window changes size -> follows, and unmount removes the listener", () => {
mockMedia([]);
const { result, unmount } = renderHook(() => usePinnedViewport());
expect(result.current).toBe(false);
act(() => {
queries[0].matches = true;
for (const listener of queries[0].listeners) listener();
});
expect(result.current).toBe(true);
unmount();
expect(queries[0].listeners.size).toBe(0);
});
});
describe("useSaveData", () => {
it.each([
["saveData on", { saveData: true }, true],
["saveData off", { saveData: false }, false],
["no connection info", undefined, false],
])("path: %s -> %s", (_label, connection, expected) => {
mockNavigator({ connection });
const { result } = renderHook(() => useSaveData());
expect(result.current).toBe(expected);
});
it("path: server render -> false (the lightest assumption is not 'save data')", () => {
mockNavigator({ connection: { saveData: true } });
function Probe() {
return <span>{String(useSaveData())}</span>;
}
expect(renderToString(<Probe />)).toContain("false");
});
});
describe("useNearViewport", () => {
type Entry = { isIntersecting: boolean };
let callbacks: Array<(entries: Entry[]) => void> = [];
let observers: Array<{
observe: jest.Mock;
disconnect: jest.Mock;
options?: IntersectionObserverInit;
}> = [];
beforeEach(() => {
callbacks = [];
observers = [];
global.IntersectionObserver = class {
observe = jest.fn();
disconnect = jest.fn();
constructor(
callback: (entries: Entry[]) => void,
options?: IntersectionObserverInit,
) {
callbacks.push(callback);
observers.push({
observe: this.observe,
disconnect: this.disconnect,
options,
});
}
} as unknown as typeof IntersectionObserver;
});
function Probe({ attach = true }: { attach?: boolean }) {
const ref = useRef<HTMLDivElement>(null);
const near = useNearViewport(ref as RefObject<Element | null>);
return (
<div>
{attach && <div ref={ref} data-testid="target" />}
<output>{String(near)}</output>
</div>
);
}
it("path: element far from the window -> false, observing it with a one-screen margin", () => {
const view = render(<Probe />);
expect(view.container.querySelector("output")).toHaveTextContent("false");
expect(observers).toHaveLength(1);
expect(observers[0].options).toEqual({ rootMargin: NEAR_MARGIN });
expect(observers[0].observe).toHaveBeenCalledWith(
view.getByTestId("target"),
);
});
it("path: the observer reports nothing intersecting -> still false", () => {
const view = render(<Probe />);
act(() => callbacks[0]([{ isIntersecting: false }]));
expect(view.container.querySelector("output")).toHaveTextContent("false");
});
it("path: the element gets within a screen -> true, and it stays true when it leaves again", () => {
const view = render(<Probe />);
act(() =>
callbacks[0]([{ isIntersecting: false }, { isIntersecting: true }]),
);
expect(view.container.querySelector("output")).toHaveTextContent("true");
act(() => callbacks[0]([{ isIntersecting: false }]));
expect(view.container.querySelector("output")).toHaveTextContent("true");
});
it("path: no ref target -> no observer is created and the answer stays false", () => {
const view = render(<Probe attach={false} />);
expect(observers).toHaveLength(0);
expect(view.container.querySelector("output")).toHaveTextContent("false");
});
it("path: browser without IntersectionObserver -> assumes near so the poster still loads", () => {
// @ts-expect-error simula um navegador sem IntersectionObserver
global.IntersectionObserver = undefined;
const view = render(<Probe />);
expect(view.container.querySelector("output")).toHaveTextContent("true");
});
it("path: unmount -> the observer is disconnected", () => {
const view = render(<Probe />);
view.unmount();
expect(observers[0].disconnect).toHaveBeenCalledTimes(1);
});
});