perf(movimento): sem tela vazia, passagens em CSS e vitrine leve
Projetos sobem sobre o palco do hero e o celular tem hero de uma tela sem mergulho; passagens com animation-timeline em CSS, sem GSAP nem motion; troca da vitrine sem texto duplicado; moldura de celular só com pôster e scrub reencodado em 960 px (9,4 para 5,2 MB); 3D monta na primeira interação e para quando parado; transição entre páginas do mesmo segmento; foco em anel duplo; texto de 15 px no celular; código morto removido.
This commit is contained in:
1 parent
10f170abf4
commit
318a764190
77 files changed
+4293
-4951
No files matched your search
@@ -1,49 +1,92 @@
|
||||
import { renderHook } from "@testing-library/react";
|
||||
import { act, renderHook } from "@testing-library/react";
|
||||
import { createElement } from "react";
|
||||
import { renderToString } from "react-dom/server";
|
||||
import { useReducedMotion } from "./useReducedMotion";
|
||||
|
||||
// Mock do Framer Motion
|
||||
jest.mock("motion/react", () => ({
|
||||
useReducedMotion: jest.fn(),
|
||||
}));
|
||||
type Listener = () => void;
|
||||
|
||||
/** matchMedia que responde só à consulta de movimento reduzido e deixa o teste mudar a preferência depois. */
|
||||
function mockPreference(initial: boolean) {
|
||||
const listeners = new Set<Listener>();
|
||||
const query = {
|
||||
matches: initial,
|
||||
media: "(prefers-reduced-motion: reduce)",
|
||||
addEventListener: jest.fn((_: string, listener: Listener) =>
|
||||
listeners.add(listener),
|
||||
),
|
||||
removeEventListener: jest.fn((_: string, listener: Listener) =>
|
||||
listeners.delete(listener),
|
||||
),
|
||||
};
|
||||
window.matchMedia = jest.fn().mockReturnValue(query);
|
||||
return {
|
||||
query,
|
||||
change(value: boolean) {
|
||||
query.matches = value;
|
||||
for (const listener of listeners) listener();
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const original = window.matchMedia;
|
||||
|
||||
afterEach(() => {
|
||||
window.matchMedia = original;
|
||||
});
|
||||
|
||||
describe("useReducedMotion", () => {
|
||||
it("deve retornar false quando prefers-reduced-motion não está ativo", () => {
|
||||
const { useReducedMotion: mockUseReducedMotion } =
|
||||
jest.requireMock("motion/react");
|
||||
mockUseReducedMotion.mockReturnValue(false);
|
||||
it("is false when the system does not ask for reduced motion", () => {
|
||||
mockPreference(false);
|
||||
|
||||
const { result } = renderHook(() => useReducedMotion());
|
||||
|
||||
expect(result.current).toBe(false);
|
||||
});
|
||||
|
||||
it("deve retornar true quando prefers-reduced-motion está ativo", () => {
|
||||
const { useReducedMotion: mockUseReducedMotion } =
|
||||
jest.requireMock("motion/react");
|
||||
mockUseReducedMotion.mockReturnValue(true);
|
||||
it("is true when the system asks for reduced motion", () => {
|
||||
mockPreference(true);
|
||||
|
||||
const { result } = renderHook(() => useReducedMotion());
|
||||
|
||||
expect(result.current).toBe(true);
|
||||
expect(window.matchMedia).toHaveBeenCalledWith(
|
||||
"(prefers-reduced-motion: reduce)",
|
||||
);
|
||||
});
|
||||
|
||||
it("deve retornar false quando o hook do Framer Motion retorna null", () => {
|
||||
const { useReducedMotion: mockUseReducedMotion } =
|
||||
jest.requireMock("motion/react");
|
||||
mockUseReducedMotion.mockReturnValue(null);
|
||||
|
||||
it("follows the preference when it changes while the page is open", () => {
|
||||
const preference = mockPreference(false);
|
||||
const { result } = renderHook(() => useReducedMotion());
|
||||
expect(result.current).toBe(false);
|
||||
|
||||
act(() => preference.change(true));
|
||||
expect(result.current).toBe(true);
|
||||
|
||||
act(() => preference.change(false));
|
||||
expect(result.current).toBe(false);
|
||||
});
|
||||
|
||||
it("deve retornar false quando o hook do Framer Motion retorna undefined", () => {
|
||||
const { useReducedMotion: mockUseReducedMotion } =
|
||||
jest.requireMock("motion/react");
|
||||
mockUseReducedMotion.mockReturnValue(undefined);
|
||||
it("stops listening when the component unmounts", () => {
|
||||
const preference = mockPreference(false);
|
||||
const { unmount } = renderHook(() => useReducedMotion());
|
||||
|
||||
unmount();
|
||||
|
||||
expect(preference.query.removeEventListener).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("is false on the server and in browsers without matchMedia, so the HTML always hydrates the same", () => {
|
||||
mockPreference(true);
|
||||
function Probe() {
|
||||
return createElement("i", { "data-reduced": String(useReducedMotion()) });
|
||||
}
|
||||
expect(renderToString(createElement(Probe))).toContain(
|
||||
'data-reduced="false"',
|
||||
);
|
||||
|
||||
// biome-ignore lint/suspicious/noExplicitAny: simula um navegador sem a API
|
||||
(window as any).matchMedia = undefined;
|
||||
const { result } = renderHook(() => useReducedMotion());
|
||||
|
||||
expect(result.current).toBe(false);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user