veza/apps/web/src/features/tracks/components/TrackListPagination.test.tsx
2025-12-12 21:34:34 -05:00

408 lines
11 KiB
TypeScript

import { describe, it, expect, vi } from 'vitest';
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { TrackListPagination } from './TrackListPagination';
describe('TrackListPagination', () => {
const mockOnPageChange = vi.fn();
beforeEach(() => {
vi.clearAllMocks();
});
it('should render pagination', () => {
render(
<TrackListPagination
currentPage={1}
totalPages={5}
totalItems={50}
itemsPerPage={10}
onPageChange={mockOnPageChange}
/>,
);
expect(
screen.getByRole('navigation', {
name: 'Pagination de la liste de pistes',
}),
).toBeInTheDocument();
});
it('should display current page information', () => {
render(
<TrackListPagination
currentPage={2}
totalPages={5}
totalItems={50}
itemsPerPage={10}
onPageChange={mockOnPageChange}
/>,
);
expect(screen.getByText(/Affichage de/)).toBeInTheDocument();
expect(screen.getByText(/11/)).toBeInTheDocument();
expect(screen.getByText(/20/)).toBeInTheDocument();
expect(screen.getByText(/50/)).toBeInTheDocument();
});
it('should display page numbers', () => {
render(
<TrackListPagination
currentPage={1}
totalPages={5}
totalItems={50}
itemsPerPage={10}
onPageChange={mockOnPageChange}
showPageNumbers={true}
/>,
);
expect(screen.getByLabelText('Aller à la page 1')).toBeInTheDocument();
expect(screen.getByLabelText('Aller à la page 2')).toBeInTheDocument();
});
it('should highlight current page', () => {
render(
<TrackListPagination
currentPage={3}
totalPages={5}
totalItems={50}
itemsPerPage={10}
onPageChange={mockOnPageChange}
/>,
);
const currentPageButton = screen.getByLabelText('Aller à la page 3');
expect(currentPageButton).toHaveAttribute('aria-current', 'page');
expect(currentPageButton).toHaveClass('bg-blue-600');
});
it('should call onPageChange when page number is clicked', async () => {
const user = userEvent.setup();
render(
<TrackListPagination
currentPage={1}
totalPages={5}
totalItems={50}
itemsPerPage={10}
onPageChange={mockOnPageChange}
/>,
);
const page2Button = screen.getByLabelText('Aller à la page 2');
await user.click(page2Button);
expect(mockOnPageChange).toHaveBeenCalledWith(2);
});
it('should call onPageChange when next button is clicked', async () => {
const user = userEvent.setup();
render(
<TrackListPagination
currentPage={1}
totalPages={5}
totalItems={50}
itemsPerPage={10}
onPageChange={mockOnPageChange}
/>,
);
const nextButton = screen.getByLabelText('Page suivante');
await user.click(nextButton);
expect(mockOnPageChange).toHaveBeenCalledWith(2);
});
it('should call onPageChange when previous button is clicked', async () => {
const user = userEvent.setup();
render(
<TrackListPagination
currentPage={2}
totalPages={5}
totalItems={50}
itemsPerPage={10}
onPageChange={mockOnPageChange}
/>,
);
const previousButton = screen.getByLabelText('Page précédente');
await user.click(previousButton);
expect(mockOnPageChange).toHaveBeenCalledWith(1);
});
it('should call onPageChange when first page button is clicked', async () => {
const user = userEvent.setup();
render(
<TrackListPagination
currentPage={3}
totalPages={5}
totalItems={50}
itemsPerPage={10}
onPageChange={mockOnPageChange}
/>,
);
const firstButton = screen.getByLabelText('Aller à la première page');
await user.click(firstButton);
expect(mockOnPageChange).toHaveBeenCalledWith(1);
});
it('should call onPageChange when last page button is clicked', async () => {
const user = userEvent.setup();
render(
<TrackListPagination
currentPage={1}
totalPages={5}
totalItems={50}
itemsPerPage={10}
onPageChange={mockOnPageChange}
/>,
);
const lastButton = screen.getByLabelText('Aller à la dernière page');
await user.click(lastButton);
expect(mockOnPageChange).toHaveBeenCalledWith(5);
});
it('should disable previous button on first page', () => {
render(
<TrackListPagination
currentPage={1}
totalPages={5}
totalItems={50}
itemsPerPage={10}
onPageChange={mockOnPageChange}
/>,
);
const previousButton = screen.getByLabelText('Page précédente');
const firstButton = screen.getByLabelText('Aller à la première page');
expect(previousButton).toBeDisabled();
expect(firstButton).toBeDisabled();
});
it('should disable next button on last page', () => {
render(
<TrackListPagination
currentPage={5}
totalPages={5}
totalItems={50}
itemsPerPage={10}
onPageChange={mockOnPageChange}
/>,
);
const nextButton = screen.getByLabelText('Page suivante');
const lastButton = screen.getByLabelText('Aller à la dernière page');
expect(nextButton).toBeDisabled();
expect(lastButton).toBeDisabled();
});
it('should show ellipsis when there are many pages', () => {
render(
<TrackListPagination
currentPage={5}
totalPages={10}
totalItems={100}
itemsPerPage={10}
onPageChange={mockOnPageChange}
maxVisiblePages={5}
/>,
);
const ellipsis = screen.queryAllByText('...');
expect(ellipsis.length).toBeGreaterThan(0);
});
it('should limit visible page numbers based on maxVisiblePages', () => {
render(
<TrackListPagination
currentPage={5}
totalPages={20}
totalItems={200}
itemsPerPage={10}
onPageChange={mockOnPageChange}
maxVisiblePages={5}
/>,
);
// Should show around 5 pages centered on current page
const pageButtons = screen.getAllByLabelText(/Aller à la page \d+/);
expect(pageButtons.length).toBeLessThanOrEqual(7); // 5 visible + first + last potentially
});
it('should hide page numbers when showPageNumbers is false', () => {
render(
<TrackListPagination
currentPage={1}
totalPages={5}
totalItems={50}
itemsPerPage={10}
onPageChange={mockOnPageChange}
showPageNumbers={false}
/>,
);
const pageButtons = screen.queryAllByLabelText(/Aller à la page \d+/);
expect(pageButtons.length).toBe(0);
});
it('should hide items info when showItemsInfo is false', () => {
render(
<TrackListPagination
currentPage={1}
totalPages={5}
totalItems={50}
itemsPerPage={10}
onPageChange={mockOnPageChange}
showItemsInfo={false}
/>,
);
expect(screen.queryByText(/Affichage de/)).not.toBeInTheDocument();
});
it('should return null when totalPages is 1 and showItemsInfo is false', () => {
const { container } = render(
<TrackListPagination
currentPage={1}
totalPages={1}
totalItems={10}
itemsPerPage={10}
onPageChange={mockOnPageChange}
showItemsInfo={false}
/>,
);
expect(container.firstChild).toBeNull();
});
it('should disable all buttons when disabled prop is true', () => {
render(
<TrackListPagination
currentPage={3}
totalPages={5}
totalItems={50}
itemsPerPage={10}
onPageChange={mockOnPageChange}
disabled={true}
/>,
);
const nextButton = screen.getByLabelText('Page suivante');
const previousButton = screen.getByLabelText('Page précédente');
const pageButton = screen.getByLabelText('Aller à la page 2');
expect(nextButton).toBeDisabled();
expect(previousButton).toBeDisabled();
expect(pageButton).toBeDisabled();
});
it('should not call onPageChange when clicking disabled buttons', async () => {
const user = userEvent.setup();
render(
<TrackListPagination
currentPage={1}
totalPages={5}
totalItems={50}
itemsPerPage={10}
onPageChange={mockOnPageChange}
/>,
);
const previousButton = screen.getByLabelText('Page précédente');
await user.click(previousButton);
expect(mockOnPageChange).not.toHaveBeenCalled();
});
it('should not call onPageChange when clicking current page', async () => {
const user = userEvent.setup();
render(
<TrackListPagination
currentPage={2}
totalPages={5}
totalItems={50}
itemsPerPage={10}
onPageChange={mockOnPageChange}
/>,
);
const currentPageButton = screen.getByLabelText('Aller à la page 2');
await user.click(currentPageButton);
expect(mockOnPageChange).not.toHaveBeenCalled();
});
it('should apply custom className', () => {
const { container } = render(
<TrackListPagination
currentPage={1}
totalPages={5}
totalItems={50}
itemsPerPage={10}
onPageChange={mockOnPageChange}
className="custom-class"
/>,
);
const nav = container.querySelector('[role="navigation"]');
expect(nav).toHaveClass('custom-class');
});
it('should calculate correct item range', () => {
render(
<TrackListPagination
currentPage={3}
totalPages={5}
totalItems={47}
itemsPerPage={10}
onPageChange={mockOnPageChange}
/>,
);
// Page 3: items 21-30, but total is 47, so 21-30
expect(screen.getByText(/21/)).toBeInTheDocument();
expect(screen.getByText(/30/)).toBeInTheDocument();
});
it('should handle edge case with last page having fewer items', () => {
render(
<TrackListPagination
currentPage={5}
totalPages={5}
totalItems={47}
itemsPerPage={10}
onPageChange={mockOnPageChange}
/>,
);
// Last page: items 41-47
expect(screen.getByText(/41/)).toBeInTheDocument();
// Check for "47" in the range display (not just anywhere)
const infoText = screen.getByText(/Affichage de/);
expect(infoText.textContent).toContain('47');
});
it('should have accessible attributes', () => {
render(
<TrackListPagination
currentPage={2}
totalPages={5}
totalItems={50}
itemsPerPage={10}
onPageChange={mockOnPageChange}
/>,
);
const nav = screen.getByRole('navigation');
expect(nav).toHaveAttribute(
'aria-label',
'Pagination de la liste de pistes',
);
const currentPageButton = screen.getByLabelText('Aller à la page 2');
expect(currentPageButton).toHaveAttribute('aria-current', 'page');
});
});