import { renderHook, act } from "@testing-library/react-native"; import { useAfterPaint } from "../useAfterPaint"; describe("useAfterPaint", () => { let frameCallbacks: FrameRequestCallback[]; let originalRaf: typeof requestAnimationFrame; let originalCancelRaf: typeof cancelAnimationFrame; const flushFrame = () => { const callbacks = frameCallbacks; frameCallbacks = []; act(() => { callbacks.forEach((cb) => cb(0)); }); }; beforeEach(() => { frameCallbacks = []; originalRaf = global.requestAnimationFrame; originalCancelRaf = global.cancelAnimationFrame; global.requestAnimationFrame = jest.fn((cb: FrameRequestCallback) => { frameCallbacks.push(cb); return frameCallbacks.length; }) as unknown as typeof requestAnimationFrame; global.cancelAnimationFrame = jest.fn(); }); afterEach(() => { global.requestAnimationFrame = originalRaf; global.cancelAnimationFrame = originalCancelRaf; }); it("returns false on the initial render", () => { const { result } = renderHook(() => useAfterPaint()); expect(result.current).toBe(false); }); it("stays false after only one animation frame (before paint completes)", () => { const { result } = renderHook(() => useAfterPaint()); flushFrame(); expect(result.current).toBe(false); }); it("returns true after two animation frames (a real post-paint boundary)", () => { const { result } = renderHook(() => useAfterPaint()); flushFrame(); flushFrame(); expect(result.current).toBe(true); }); });