import { renderHook, act } from '@testing-library/react' import { usePagination } from '../../hooks/usePagination' describe('usePagination', () => { const defaultProps = { totalCount: 100, initialPageSize: 25, initialPage: 1, } describe('Initialization', () => { it('should initialize with default values', () => { const { result } = renderHook(() => usePagination(defaultProps)) expect(result.current.currentPage).toBe(1) expect(result.current.pageSize).toBe(25) expect(result.current.totalPages).toBe(4) expect(result.current.totalCount).toBe(100) }) it('should initialize with custom page size', () => { const { result } = renderHook(() => usePagination({ ...defaultProps, initialPageSize: 50 }) ) expect(result.current.pageSize).toBe(50) expect(result.current.totalPages).toBe(2) }) it('should initialize with custom initial page', () => { const { result } = renderHook(() => usePagination({ ...defaultProps, initialPage: 2 }) ) expect(result.current.currentPage).toBe(2) }) }) describe('Total Pages Calculation', () => { it('should calculate total pages correctly', () => { const { result } = renderHook(() => usePagination({ ...defaultProps, totalCount: 100, initialPageSize: 25 }) ) expect(result.current.totalPages).toBe(4) }) it('should round up for partial pages', () => { const { result } = renderHook(() => usePagination({ ...defaultProps, totalCount: 101, initialPageSize: 25 }) ) expect(result.current.totalPages).toBe(5) }) it('should return minimum 1 page for empty data', () => { const { result } = renderHook(() => usePagination({ ...defaultProps, totalCount: 0 }) ) expect(result.current.totalPages).toBe(1) }) it('should handle single item', () => { const { result } = renderHook(() => usePagination({ ...defaultProps, totalCount: 1 }) ) expect(result.current.totalPages).toBe(1) }) }) describe('Navigation - Can Go', () => { it('should not allow going previous from first page', () => { const { result } = renderHook(() => usePagination(defaultProps)) expect(result.current.canGoPrevious).toBe(false) expect(result.current.canGoNext).toBe(true) }) it('should not allow going next from last page', () => { const { result } = renderHook(() => usePagination({ ...defaultProps, initialPage: 4 }) ) expect(result.current.canGoNext).toBe(false) expect(result.current.canGoPrevious).toBe(true) }) it('should allow going both directions from middle page', () => { const { result } = renderHook(() => usePagination({ ...defaultProps, initialPage: 2 }) ) expect(result.current.canGoNext).toBe(true) expect(result.current.canGoPrevious).toBe(true) }) it('should update canGo flags when navigating', () => { const { result } = renderHook(() => usePagination(defaultProps)) expect(result.current.canGoPrevious).toBe(false) act(() => { result.current.goToNextPage() }) expect(result.current.canGoPrevious).toBe(true) }) }) describe('Navigation - Go To Page', () => { it('should go to specific page', () => { const { result } = renderHook(() => usePagination(defaultProps)) act(() => { result.current.goToPage(3) }) expect(result.current.currentPage).toBe(3) }) it('should clamp to valid page range', () => { const { result } = renderHook(() => usePagination(defaultProps)) act(() => { result.current.goToPage(10) }) expect(result.current.currentPage).toBe(4) // Max page act(() => { result.current.goToPage(-5) }) expect(result.current.currentPage).toBe(1) // Min page }) it('should call onPageChange callback', () => { const onPageChange = jest.fn() const { result } = renderHook(() => usePagination({ ...defaultProps, onPageChange }) ) act(() => { result.current.goToPage(2) }) expect(onPageChange).toHaveBeenCalledWith(2) }) }) describe('Navigation - First/Last Page', () => { it('should go to first page', () => { const { result } = renderHook(() => usePagination({ ...defaultProps, initialPage: 3 }) ) act(() => { result.current.goToFirstPage() }) expect(result.current.currentPage).toBe(1) }) it('should go to last page', () => { const { result } = renderHook(() => usePagination(defaultProps)) act(() => { result.current.goToLastPage() }) expect(result.current.currentPage).toBe(4) }) }) describe('Navigation - Next/Previous Page', () => { it('should go to next page', () => { const { result } = renderHook(() => usePagination(defaultProps)) act(() => { result.current.goToNextPage() }) expect(result.current.currentPage).toBe(2) }) it('should go to previous page', () => { const { result } = renderHook(() => usePagination({ ...defaultProps, initialPage: 2 }) ) act(() => { result.current.goToPreviousPage() }) expect(result.current.currentPage).toBe(1) }) it('should not go past last page', () => { const { result } = renderHook(() => usePagination({ ...defaultProps, initialPage: 4 }) ) act(() => { result.current.goToNextPage() }) expect(result.current.currentPage).toBe(4) // Should stay on last page }) it('should not go before first page', () => { const { result } = renderHook(() => usePagination(defaultProps)) act(() => { result.current.goToPreviousPage() }) expect(result.current.currentPage).toBe(1) // Should stay on first page }) }) describe('Page Numbers Display', () => { it('should show all pages when total is small', () => { const { result } = renderHook(() => usePagination({ ...defaultProps, totalCount: 50, initialPageSize: 25 }) ) const pageNumbers = result.current.getPageNumbers() expect(pageNumbers).toEqual([1, 2]) }) it('should show ellipsis for many pages', () => { const { result } = renderHook(() => usePagination({ ...defaultProps, totalCount: 200, initialPageSize: 10 }) ) const pageNumbers = result.current.getPageNumbers() expect(pageNumbers).toContain('...') expect(pageNumbers[0]).toBe(1) expect(pageNumbers[pageNumbers.length - 1]).toBe(20) }) it('should show current page and neighbors', () => { const { result } = renderHook(() => usePagination({ ...defaultProps, totalCount: 200, initialPageSize: 10, initialPage: 10, }) ) const pageNumbers = result.current.getPageNumbers() expect(pageNumbers).toContain(9) expect(pageNumbers).toContain(10) expect(pageNumbers).toContain(11) }) it('should update page numbers when navigating', () => { const { result } = renderHook(() => usePagination({ ...defaultProps, totalCount: 200, initialPageSize: 10 }) ) act(() => { result.current.goToPage(15) }) const pageNumbers = result.current.getPageNumbers() expect(pageNumbers).toContain(14) expect(pageNumbers).toContain(15) expect(pageNumbers).toContain(16) }) it('should handle first page differently', () => { const { result } = renderHook(() => usePagination({ ...defaultProps, totalCount: 200, initialPageSize: 10 }) ) const pageNumbers = result.current.getPageNumbers() expect(pageNumbers[0]).toBe(1) }) it('should handle last page differently', () => { const { result } = renderHook(() => usePagination({ ...defaultProps, totalCount: 200, initialPageSize: 10, initialPage: 20, }) ) const pageNumbers = result.current.getPageNumbers() expect(pageNumbers[pageNumbers.length - 1]).toBe(20) }) }) describe('Display Range', () => { it('should calculate display range for first page', () => { const { result } = renderHook(() => usePagination(defaultProps)) const range = result.current.getDisplayRange() expect(range).toEqual({ start: 1, end: 25 }) }) it('should calculate display range for middle page', () => { const { result } = renderHook(() => usePagination({ ...defaultProps, initialPage: 2 }) ) const range = result.current.getDisplayRange() expect(range).toEqual({ start: 26, end: 50 }) }) it('should calculate display range for last page with partial data', () => { const { result } = renderHook(() => usePagination({ ...defaultProps, totalCount: 90, initialPage: 4 }) ) const range = result.current.getDisplayRange() expect(range).toEqual({ start: 76, end: 90 }) }) it('should handle single item on last page', () => { const { result } = renderHook(() => usePagination({ ...defaultProps, totalCount: 76, initialPage: 4 }) ) const range = result.current.getDisplayRange() expect(range).toEqual({ start: 76, end: 76 }) }) it('should update range when navigating', () => { const { result } = renderHook(() => usePagination(defaultProps)) act(() => { result.current.goToPage(3) }) const range = result.current.getDisplayRange() expect(range).toEqual({ start: 51, end: 75 }) }) }) describe('Server-Side Pagination', () => { it('should handle server-side mode', () => { const onPageChange = jest.fn() const { result } = renderHook(() => usePagination({ ...defaultProps, serverSide: true, onPageChange }) ) act(() => { result.current.goToPage(2) }) expect(onPageChange).toHaveBeenCalledWith(2) }) it('should work the same as client-side for navigation', () => { const { result } = renderHook(() => usePagination({ ...defaultProps, serverSide: true }) ) act(() => { result.current.goToNextPage() }) expect(result.current.currentPage).toBe(2) }) }) describe('Edge Cases', () => { it('should handle zero total count', () => { const { result } = renderHook(() => usePagination({ ...defaultProps, totalCount: 0 }) ) expect(result.current.totalPages).toBe(1) expect(result.current.currentPage).toBe(1) expect(result.current.canGoNext).toBe(false) expect(result.current.canGoPrevious).toBe(false) }) it('should handle large page sizes', () => { const { result } = renderHook(() => usePagination({ ...defaultProps, initialPageSize: 1000 }) ) expect(result.current.totalPages).toBe(1) const range = result.current.getDisplayRange() expect(range).toEqual({ start: 1, end: 100 }) }) it('should handle page size changes dynamically', () => { const { result, rerender } = renderHook( ({ pageSize }) => usePagination({ ...defaultProps, initialPageSize: pageSize }), { initialProps: { pageSize: 25 } } ) expect(result.current.totalPages).toBe(4) rerender({ pageSize: 50 }) // Note: Since initialPageSize is only used for initialization, // the totalPages will stay the same until internal state is updated // This test documents current behavior expect(result.current.totalPages).toBe(4) }) }) describe('Memoization', () => { it('should memoize computed values', () => { const { result, rerender } = renderHook(() => usePagination(defaultProps)) const firstPageNumbers = result.current.getPageNumbers() rerender() const secondPageNumbers = result.current.getPageNumbers() // Functions should return consistent results expect(firstPageNumbers).toEqual(secondPageNumbers) }) it('should update memoized values when dependencies change', () => { const { result } = renderHook(() => usePagination(defaultProps)) const firstCanGoNext = result.current.canGoNext act(() => { result.current.goToLastPage() }) const secondCanGoNext = result.current.canGoNext expect(firstCanGoNext).toBe(true) expect(secondCanGoNext).toBe(false) }) }) })