import { toBasicISOString, toLocalISOString } from '@douglasneuroinformatics/libjs'; import { getByText, render, screen } from '@testing-library/react'; import { userEvent } from '@testing-library/user-event'; import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { DateField } from './DateField.tsx'; describe('DateField', () => { const setError = vi.fn(); const setValue = vi.fn(); beforeEach(() => { vi.useFakeTimers({ toFake: ['Date'] }); vi.setSystemTime(new Date(2025, 0, 1, 22)); }); afterEach(() => { vi.useRealTimers(); vi.clearAllMocks(); }); it('should render an input with an empty string value ', () => { render(); const input: HTMLInputElement = screen.getByRole('textbox'); expect(input).toBeInTheDocument(); expect(input.value).toBe(''); }); it('should allow typing arbitrary text while focused', async () => { render(); const input: HTMLInputElement = screen.getByRole('textbox'); await userEvent.type(input, 'foo'); expect(input.value).toBe('foo'); }); it('should not initially show the datepicker', () => { render(); expect(() => screen.getByTestId('datepicker')).toThrow(); }); it('should show the datepicker when the input is clicked', async () => { render(); const input = screen.getByRole('textbox'); expect(() => screen.getByTestId('datepicker')).toThrow(); await userEvent.click(input); expect(screen.getByTestId('datepicker')).toBeInTheDocument(); }); it('should discard invalid text when unfocused, once the date picker is closed', async () => { render(); const input: HTMLInputElement = screen.getByRole('textbox'); await userEvent.type(input, 'foo'); expect(input.value).toBe('foo'); const datepicker = screen.getByTestId('datepicker'); await userEvent.click(datepicker); expect(datepicker).toBeInTheDocument(); await userEvent.type(input, 'foo'); await userEvent.click(screen.getByText('Date')); expect(() => screen.getByTestId('datepicker')).toThrow(); expect(input.value).toBe(''); }); it('should not attempt to set the value if the date is invalid', async () => { render(); const input: HTMLInputElement = screen.getByRole('textbox'); await userEvent.type(input, 'foo'); expect(input.value).toBe('foo'); await userEvent.click(screen.getByText('Date')); expect(input.value).toBe(''); expect(setValue).not.toHaveBeenCalled(); }); it('should attempt to set the value if the user enters a correct date', async () => { render(); const input: HTMLInputElement = screen.getByRole('textbox'); await userEvent.type(input, '2000-01-01'); await userEvent.click(screen.getByText('Date')); expect(input.value).toBe('2000-01-01'); expect(setValue).toHaveBeenCalledOnce(); expect(setValue).toHaveBeenCalledWith(new Date('2000-01-01')); }); it('should allow setting the date using the date picker', async () => { render(); const input: HTMLInputElement = screen.getByRole('textbox'); let datepicker: HTMLElement; let expectedDate: Date; let expectedDateString: string; await userEvent.click(input); datepicker = screen.getByTestId('datepicker'); await userEvent.click(getByText(datepicker, '1')); expectedDate = new Date(new Date().setDate(1)); expectedDateString = toLocalISOString(expectedDate).split('T')[0]!; expect(toBasicISOString(setValue.mock.lastCall?.[0])).toBe(expectedDateString); await userEvent.click(input); datepicker = screen.getByTestId('datepicker'); await userEvent.click(getByText(datepicker, '2')); expectedDate = new Date(new Date().setDate(2)); expectedDateString = toLocalISOString(expectedDate).split('T')[0]!; expect(toBasicISOString(setValue.mock.lastCall?.[0])).toBe(expectedDateString); }); it('should render the value provided as a prop', () => { const today = new Date(); render(); const input: HTMLInputElement = screen.getByRole('textbox'); expect(input.value).toBe(toBasicISOString(today)); }); });