import type { MutableRefObject, RefObject } from 'react'; import { render } from '@testing-library/react'; import { useRef } from 'react'; import { describe, expect, it, vi } from 'vitest'; import { composeRefs, useComposedRefs } from '../use-composed-refs'; // Helper to create a mutable ref object for testing (without using deprecated createRef) function createMutableRef(initialValue: T | null = null): MutableRefObject { return { current: initialValue }; } describe('composeRefs', () => { it('sets value on callback ref', () => { const callbackRef = vi.fn(); const composed = composeRefs(callbackRef); composed('test-value'); expect(callbackRef).toHaveBeenCalledWith('test-value'); }); it('sets value on RefObject', () => { const refObject = createMutableRef(); const composed = composeRefs(refObject); composed('test-value'); expect(refObject.current).toBe('test-value'); }); it('sets value on multiple refs', () => { const callbackRef = vi.fn(); const refObject = createMutableRef(); const composed = composeRefs(callbackRef, refObject); composed('test-value'); expect(callbackRef).toHaveBeenCalledWith('test-value'); expect(refObject.current).toBe('test-value'); }); it('handles undefined refs', () => { const callbackRef = vi.fn(); const composed = composeRefs(undefined, callbackRef, undefined); composed('test-value'); expect(callbackRef).toHaveBeenCalledWith('test-value'); }); it('returns cleanup function when callback ref returns one', () => { const cleanup = vi.fn(); const callbackRef = vi.fn().mockReturnValue(cleanup); const composed = composeRefs(callbackRef); const returnedCleanup = composed('test-value') as (() => void) | void; expect(returnedCleanup).toBeTypeOf('function'); if (typeof returnedCleanup === 'function') { returnedCleanup(); } expect(cleanup).toHaveBeenCalled(); }); it('clears RefObject on cleanup', () => { const cleanup = vi.fn(); const callbackRef = vi.fn().mockReturnValue(cleanup); const refObject = createMutableRef(); const composed = composeRefs(callbackRef, refObject); composed('test-value'); expect(refObject.current).toBe('test-value'); const returnedCleanup = composed('test-value') as (() => void) | void; if (typeof returnedCleanup === 'function') { returnedCleanup(); } expect(refObject.current).toBeNull(); }); }); describe('useComposedRefs', () => { it('returns a stable callback ref', () => { let composedRef1: ((value: HTMLDivElement | null) => void) | null = null; let composedRef2: ((value: HTMLDivElement | null) => void) | null = null; function TestComponent() { const ref1 = useRef(null); const ref2 = useRef(null); const composed = useComposedRefs(ref1, ref2); if (!composedRef1) { composedRef1 = composed; } else { composedRef2 = composed; } return
Test
; } const { rerender } = render(); rerender(); // Same refs should produce same composed ref expect(composedRef1).toBe(composedRef2); }); it('works with forwardRef pattern', () => { const externalRef = createMutableRef(); function TestComponent({ forwardedRef }: { forwardedRef: RefObject }) { const internalRef = useRef(null); const composedRef = useComposedRefs(forwardedRef, internalRef); return
Test
; } render(); expect(externalRef.current).toBeInstanceOf(HTMLDivElement); }); });