import { render, screen, waitFor } from '@testing-library/react'; import { describe, it, expect, vi, beforeEach } from 'vitest'; import userEvent from '@testing-library/user-event'; import { DatePicker } from './date-picker'; describe('DatePicker Component', () => { const mockOnChange = vi.fn(); beforeEach(() => { vi.clearAllMocks(); }); it('renders date picker trigger correctly', () => { render(); expect(screen.getByText('Select date...')).toBeInTheDocument(); }); it('uses custom placeholder', () => { render(); expect(screen.getByText('Choose a date')).toBeInTheDocument(); }); it('displays selected date in single mode', () => { const date = new Date(2024, 0, 15); render(); expect(screen.getByText(date.toLocaleDateString())).toBeInTheDocument(); }); it('displays selected date range', () => { const start = new Date(2024, 0, 15); const end = new Date(2024, 0, 20); render( , ); // eslint-disable-next-line @typescript-eslint/no-unused-vars const displayText = `${start.toLocaleDateString()} - ${end.toLocaleDateString()}`; expect( screen.getByText( new RegExp(start.toLocaleDateString().replace(/\//g, '/')), ), ).toBeInTheDocument(); }); it('opens calendar when trigger is clicked', async () => { const user = userEvent.setup(); render(); const triggers = screen.getAllByRole('button'); const trigger = triggers.find((btn) => btn.textContent?.includes('Select date...')) || triggers[0]; await user.click(trigger); await waitFor(() => { expect(screen.getByText('Today')).toBeInTheDocument(); }); }); it('displays current month by default', async () => { const user = userEvent.setup(); const now = new Date(); render(); const triggers = screen.getAllByRole('button'); const trigger = triggers.find((btn) => btn.textContent?.includes('Select date...')) || triggers[0]; await user.click(trigger); await waitFor(() => { const monthNames = [ 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December', ]; const monthText = `${monthNames[now.getMonth()]} ${now.getFullYear()}`; expect(screen.getByText(monthText)).toBeInTheDocument(); }); }); it('navigates to previous month', async () => { const user = userEvent.setup(); const now = new Date(); const prevMonth = now.getMonth() === 0 ? 11 : now.getMonth() - 1; const prevYear = now.getMonth() === 0 ? now.getFullYear() - 1 : now.getFullYear(); render(); const triggers = screen.getAllByRole('button'); const trigger = triggers.find((btn) => btn.textContent?.includes('Select date...')) || triggers[0]; await user.click(trigger); await waitFor(() => { expect(screen.getByText('Today')).toBeInTheDocument(); }); const prevButton = screen.getAllByRole('button').find((btn) => { const svg = btn.querySelector('svg'); return svg && svg.getAttribute('d')?.includes('m15 18-6-6 6-6'); }); if (prevButton) { await user.click(prevButton); await waitFor(() => { const monthNames = [ 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December', ]; const monthText = `${monthNames[prevMonth]} ${prevYear}`; expect(screen.getByText(monthText)).toBeInTheDocument(); }); } }); it('navigates to next month', async () => { const user = userEvent.setup(); const now = new Date(); const nextMonth = now.getMonth() === 11 ? 0 : now.getMonth() + 1; const nextYear = now.getMonth() === 11 ? now.getFullYear() + 1 : now.getFullYear(); render(); const triggers = screen.getAllByRole('button'); const trigger = triggers.find((btn) => btn.textContent?.includes('Select date...')) || triggers[0]; await user.click(trigger); await waitFor(() => { expect(screen.getByText('Today')).toBeInTheDocument(); }); const nextButtons = screen.getAllByRole('button'); const nextButton = nextButtons.find((btn) => { const svg = btn.querySelector('svg'); return svg && btn.getAttribute('aria-label') !== 'Fermer'; }); // Chercher le bouton avec ChevronRight const chevronRightButtons = Array.from( document.querySelectorAll('svg'), ).filter((svg) => { const path = svg.querySelector('path'); return path && path.getAttribute('d')?.includes('m9 18 6-6-6-6'); }); if (chevronRightButtons.length > 0) { const nextBtn = chevronRightButtons[0].closest('button'); if (nextBtn) { await user.click(nextBtn); await waitFor(() => { const monthNames = [ 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December', ]; const monthText = `${monthNames[nextMonth]} ${nextYear}`; expect(screen.getByText(monthText)).toBeInTheDocument(); }); } } }); it('selects a date in single mode', async () => { const user = userEvent.setup(); render(); const triggers = screen.getAllByRole('button'); const trigger = triggers.find((btn) => btn.textContent?.includes('Select date...')) || triggers[0]; await user.click(trigger); await waitFor(() => { expect(screen.getByText('Today')).toBeInTheDocument(); }); // Sélectionner le jour 15 const day15 = screen.getByText('15'); if (day15) { await user.click(day15); expect(mockOnChange).toHaveBeenCalled(); const selectedDate = mockOnChange.mock.calls[0][0]; expect(selectedDate).toBeInstanceOf(Date); } }); it('selects date range', async () => { const user = userEvent.setup(); render(); const triggers = screen.getAllByRole('button'); const trigger = triggers.find((btn) => btn.textContent?.includes('Select date range...'), ) || triggers[0]; await user.click(trigger); await waitFor(() => { expect(screen.getByText('Today')).toBeInTheDocument(); }); // Sélectionner le jour 15 const day15 = screen.getByText('15'); if (day15) { await user.click(day15); expect(mockOnChange).toHaveBeenCalled(); const firstCall = mockOnChange.mock.calls[0][0]; expect(firstCall).toHaveProperty('start'); expect(firstCall).toHaveProperty('end'); } }); it('completes date range selection', async () => { const user = userEvent.setup(); const startDate = new Date(2024, 0, 15); render( , ); const triggers = screen.getAllByRole('button'); const trigger = triggers.find((btn) => btn.textContent?.includes('15')) || triggers[0]; await user.click(trigger); await waitFor(() => { expect(screen.getByText('Today')).toBeInTheDocument(); }); // Sélectionner le jour 20 pour compléter la range const day20 = screen.getByText('20'); if (day20) { await user.click(day20); expect(mockOnChange).toHaveBeenCalled(); const call = mockOnChange.mock.calls[mockOnChange.mock.calls.length - 1][0]; expect(call).toHaveProperty('start'); expect(call).toHaveProperty('end'); expect(call.start).toBeInstanceOf(Date); expect(call.end).toBeInstanceOf(Date); } }); it('disables dates before minDate', async () => { const user = userEvent.setup(); const minDate = new Date(2024, 0, 15); render(); const triggers = screen.getAllByRole('button'); const trigger = triggers.find((btn) => btn.textContent?.includes('Select date...')) || triggers[0]; await user.click(trigger); await waitFor(() => { expect(screen.getByText('Today')).toBeInTheDocument(); }); // Le jour 1 devrait être désactivé (avant le 15) const day1 = screen.queryByText('1'); if (day1) { const dayButton = day1.closest('button'); // Vérifier que le bouton est désactivé ou a les classes de désactivation expect( dayButton?.classList.contains('opacity-50') || dayButton?.classList.contains('cursor-not-allowed') || dayButton?.hasAttribute('disabled'), ).toBe(true); } }); it('disables dates after maxDate', async () => { const user = userEvent.setup(); const maxDate = new Date(2024, 0, 15); render(); const triggers = screen.getAllByRole('button'); const trigger = triggers.find((btn) => btn.textContent?.includes('Select date...')) || triggers[0]; await user.click(trigger); await waitFor(() => { expect(screen.getByText('Today')).toBeInTheDocument(); }); // Le jour 31 devrait être désactivé si après maxDate const day31 = screen.queryByText('31'); if (day31) { const dayButton = day31.closest('button'); if (dayButton) { expect(dayButton).toHaveClass('opacity-50'); } } }); it('clears selection when clear button is clicked', async () => { const user = userEvent.setup(); const date = new Date(2024, 0, 15); render(); const clearButton = screen.getByTestId('date-picker-clear'); await user.click(clearButton); await waitFor(() => { expect(mockOnChange).toHaveBeenCalled(); }); const lastCall = mockOnChange.mock.calls[mockOnChange.mock.calls.length - 1]; expect(lastCall[0] === undefined || lastCall[0] === null).toBe(true); }); it('selects today when Today button is clicked', async () => { const user = userEvent.setup(); render(); const triggers = screen.getAllByRole('button'); const trigger = triggers.find((btn) => btn.textContent?.includes('Select date...')) || triggers[0]; await user.click(trigger); await waitFor(() => { expect(screen.getByText('Today')).toBeInTheDocument(); }); const todayButton = screen.getByText('Today'); await user.click(todayButton); expect(mockOnChange).toHaveBeenCalled(); const selectedDate = mockOnChange.mock.calls[0][0]; expect(selectedDate).toBeInstanceOf(Date); }); it('disables date picker when disabled prop is true', () => { render(); const buttons = document.querySelectorAll('button'); const selectButton = Array.from(buttons).find( (btn) => btn.textContent?.includes('Select date...') && btn.disabled, ); expect(selectButton).toBeInTheDocument(); }); it('displays days of week correctly', async () => { const user = userEvent.setup(); render(); const triggers = screen.getAllByRole('button'); const trigger = triggers.find((btn) => btn.textContent?.includes('Select date...')) || triggers[0]; await user.click(trigger); await waitFor(() => { expect(screen.getByText('Mon')).toBeInTheDocument(); expect(screen.getByText('Tue')).toBeInTheDocument(); expect(screen.getByText('Wed')).toBeInTheDocument(); expect(screen.getByText('Thu')).toBeInTheDocument(); expect(screen.getByText('Fri')).toBeInTheDocument(); expect(screen.getByText('Sat')).toBeInTheDocument(); expect(screen.getByText('Sun')).toBeInTheDocument(); }); }); });