import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import DateSelector from './DateSelector';
const dateMock = new Date(Date.UTC(2022, 8, 24, 0, 0, 0, 0));
jest.setSystemTime(dateMock);
Date.now = jest.fn(() => dateMock.valueOf());
jest.spyOn(Date, 'now').mockImplementation(() => dateMock.valueOf());
describe('DateSelector', () => {
const mockOnChange = jest.fn();
beforeEach(() => {
mockOnChange.mockClear();
});
it('renders DateSelector with empty inputs when no defaultDate provided', () => {
render();
const dayInput = screen.getByLabelText(/day/i);
const monthInput = screen.getByLabelText(/month/i);
const yearInput = screen.getByLabelText(/year/i);
expect(dayInput).toHaveValue(null); // number input value is null/empty string
expect(monthInput).toHaveValue(null);
expect(yearInput).toHaveValue(null);
// Should call onChange with undefined initially or not at all if we want strict behavior
// Based on implementation: useEffect triggers on mount. If empty, calls onChange(undefined).
expect(mockOnChange).toHaveBeenCalledWith(undefined);
});
it('renders DateSelector with default value correctly', () => {
const defaultDate = new Date(Date.now());
render(
);
const dayInput = screen.getByLabelText(/day/i);
const monthInput = screen.getByLabelText(/month/i);
const yearInput = screen.getByLabelText(/year/i);
expect(dayInput).toHaveValue(defaultDate.getDate());
expect(monthInput).toHaveValue(defaultDate.getMonth() + 1);
expect(yearInput).toHaveValue(defaultDate.getFullYear());
});
describe('Desktop View', () => {
it('updates day and calls onChange with undefined if incomplete', () => {
render();
const dayInput = screen.getByLabelText(/day/i);
fireEvent.change(dayInput, { target: { value: '15' } });
expect(dayInput).toHaveValue(15);
expect(mockOnChange).toHaveBeenCalledWith(undefined);
});
it('calls onChange with valid date when all fields are filled', () => {
render();
const dayInput = screen.getByLabelText(/day/i);
const monthInput = screen.getByLabelText(/month/i);
const yearInput = screen.getByLabelText(/year/i);
fireEvent.change(dayInput, { target: { value: '15' } });
fireEvent.change(monthInput, { target: { value: '5' } });
fireEvent.change(yearInput, { target: { value: '1990' } });
expect(mockOnChange).toHaveBeenCalledWith(
expect.objectContaining({
year: 1990,
month: 5,
day: 15,
})
);
});
it('auto-focuses month after day is filled', () => {
render();
const dayInput = screen.getByLabelText(/day/i);
const monthInput = screen.getByLabelText(/month/i);
fireEvent.change(dayInput, { target: { value: '12' } });
expect(monthInput).toHaveFocus();
});
it('auto-focuses year after month is filled', () => {
render();
const monthInput = screen.getByLabelText(/month/i);
const yearInput = screen.getByLabelText(/year/i);
fireEvent.change(monthInput, { target: { value: '12' } });
expect(yearInput).toHaveFocus();
});
it('disables all inputs when disabled prop is true', () => {
render();
const dayInput = screen.getByLabelText(/day/i);
const monthInput = screen.getByLabelText(/month/i);
const yearInput = screen.getByLabelText(/year/i);
expect(dayInput).toBeDisabled();
expect(monthInput).toBeDisabled();
expect(yearInput).toBeDisabled();
});
});
describe('Mobile View', () => {
it('renders native date input on mobile', () => {
render();
const dateInput = screen.getByLabelText(/date/i);
expect(dateInput).toBeInTheDocument();
expect(dateInput).toHaveAttribute('type', 'date');
});
it('calls onChange when mobile date input changes', async () => {
render();
const dateInput = screen.getByLabelText(/date/i);
fireEvent.change(dateInput, { target: { value: '1995-06-15' } });
await waitFor(() => {
expect(mockOnChange).toHaveBeenCalledWith(
expect.objectContaining({
year: 1995,
month: 6,
day: 15
})
);
});
});
it('calls onChange with undefined when mobile input is cleared', async () => {
render();
const dateInput = screen.getByLabelText(/date/i);
fireEvent.change(dateInput, { target: { value: '' } });
await waitFor(() => {
expect(mockOnChange).toHaveBeenCalledWith(undefined);
});
});
});
});