import '@testing-library/jest-dom' import { fireEvent, render } from '@testing-library/react' import { vi } from 'vitest' import { IPktDatepicker, PktDatepicker } from './Datepicker' const datePickerId = 'datepickerId' const label = 'Date Picker Label' const createDatepickerTest = (props: Partial = {}) => { const defaultProps: IPktDatepicker = { label, id: datePickerId, ...props, } return render() } describe('PktDatepicker', () => { describe('Date boundary testing', () => { test('handles minimum possible dates', () => { const { container } = createDatepickerTest({ value: '1900-01-01', }) const input = container.querySelector('input[type="date"]') as HTMLInputElement expect(input.value).toBe('1900-01-01') }) test('handles maximum possible dates', () => { const { container } = createDatepickerTest({ value: '2100-12-31', }) const input = container.querySelector('input[type="date"]') as HTMLInputElement expect(input.value).toBe('2100-12-31') }) test('handles year boundaries correctly', () => { const { container } = createDatepickerTest({ value: '2024-12-31', }) const input = container.querySelector('input[type="date"]') as HTMLInputElement expect(input.value).toBe('2024-12-31') }) test('handles month boundaries correctly', () => { const { container } = createDatepickerTest({ value: '2024-01-31', }) const input = container.querySelector('input[type="date"]') as HTMLInputElement expect(input.value).toBe('2024-01-31') }) test('handles leap year dates correctly', () => { const { container } = createDatepickerTest({ value: '2024-02-29', }) const input = container.querySelector('input[type="date"]') as HTMLInputElement expect(input.value).toBe('2024-02-29') }) test.each([ { desc: 'date before min', min: '2024-06-01', max: '2024-06-30', value: '2024-05-15' }, { desc: 'date after max', min: '2024-06-01', max: '2024-06-30', value: '2024-07-15' }, { desc: 'valid date within range', min: '2024-06-01', max: '2024-06-30', value: '2024-06-15' }, ])('renders with $desc ($value) without crashing', ({ min, max, value }) => { const { container } = createDatepickerTest({ min, max, value, }) const input = container.querySelector('input[type="date"]') as HTMLInputElement expect(input.value).toBe(value) expect(input).toHaveAttribute('min', min) expect(input).toHaveAttribute('max', max) }) }) describe('Error handling and edge cases', () => { test('handles empty values correctly', () => { const { container } = createDatepickerTest({ value: '', }) const input = container.querySelector('input[type="date"]') as HTMLInputElement expect(input.value).toBe('') }) test('handles undefined value correctly', () => { const { container } = createDatepickerTest({ value: undefined, }) const input = container.querySelector('input[type="date"]') as HTMLInputElement expect(input.value).toBe('') }) test('handles empty array value for multiple', () => { const { container } = createDatepickerTest({ multiple: true, value: [], }) const tags = container.querySelectorAll('.pkt-date-tags .pkt-tag') expect(tags.length).toBe(0) }) test('handles single-value array for range mode', () => { const { container } = createDatepickerTest({ range: true, value: ['2024-06-15'], }) const inputs = container.querySelectorAll('input[type="date"]') as NodeListOf expect(inputs[0].value).toBe('2024-06-15') expect(inputs[1].value).toBe('') }) test('handles many dates with maxlength', () => { // Generate 20 unique dates const dates = Array.from({ length: 20 }, (_, i) => { const day = String(i + 1).padStart(2, '0') return `2024-06-${day}` }) const { container } = createDatepickerTest({ multiple: true, maxlength: 10, value: dates, }) // Should render all dates as tags (maxlength only restricts new additions) const tags = container.querySelectorAll('.pkt-date-tags .pkt-tag') expect(tags.length).toBe(dates.length) // Input should be disabled since we're over maxlength const input = container.querySelector('input[type="date"]') as HTMLInputElement expect(input).toBeDisabled() }) test('handles conflicting properties gracefully (both multiple and range)', () => { // When both multiple and range are set, multiple takes precedence const { container } = createDatepickerTest({ multiple: true, range: true, value: ['2024-06-15', '2024-06-20'], }) // Multiple mode should render tags const tags = container.querySelectorAll('.pkt-date-tags .pkt-tag') expect(tags.length).toBe(2) // Should have only one input (multiple mode), not two (range mode) const inputs = container.querySelectorAll('input[type="date"]') expect(inputs.length).toBe(1) }) test('handles string value passed as array', () => { const { container } = createDatepickerTest({ value: '2024-06-15', }) const input = container.querySelector('input[type="date"]') as HTMLInputElement expect(input.value).toBe('2024-06-15') }) test('handles array value passed for single mode', () => { const { container } = createDatepickerTest({ value: ['2024-06-15'], }) const input = container.querySelector('input[type="date"]') as HTMLInputElement expect(input.value).toBe('2024-06-15') }) }) describe('Controlled vs uncontrolled', () => { test('works as controlled component', () => { const { container, rerender } = render( , ) let input = container.querySelector('input[type="date"]') as HTMLInputElement expect(input.value).toBe('2024-06-15') rerender() input = container.querySelector('input[type="date"]') as HTMLInputElement expect(input.value).toBe('2024-07-20') }) test('works as uncontrolled component', () => { const { container } = createDatepickerTest() const input = container.querySelector('input[type="date"]') as HTMLInputElement expect(input.value).toBe('') }) test('controlled range updates both inputs on rerender', () => { const { container, rerender } = render( , ) const inputs = container.querySelectorAll('input[type="date"]') as NodeListOf expect(inputs[0].value).toBe('2024-06-15') expect(inputs[1].value).toBe('2024-06-25') rerender( , ) const updatedInputs = container.querySelectorAll('input[type="date"]') as NodeListOf expect(updatedInputs[0].value).toBe('2024-07-01') expect(updatedInputs[1].value).toBe('2024-07-31') }) test('controlled multiple updates tags on rerender', () => { const { container, rerender } = render( , ) let tags = container.querySelectorAll('.pkt-date-tags .pkt-tag') expect(tags.length).toBe(1) rerender( , ) tags = container.querySelectorAll('.pkt-date-tags .pkt-tag') expect(tags.length).toBe(3) }) test('calendarOpen prop controls calendar state', () => { const { container, rerender } = render( , ) let popup = container.querySelector('.pkt-calendar-popup') expect(popup).toHaveAttribute('hidden') rerender() popup = container.querySelector('.pkt-calendar-popup') expect(popup).not.toHaveAttribute('hidden') }) }) describe('Form integration', () => { test('form input has name and value when name is set', () => { const { container } = createDatepickerTest({ name: 'dateField', value: '2024-06-15', }) const formInput = container.querySelector('input[aria-hidden="true"]') as HTMLInputElement expect(formInput).toBeInTheDocument() expect(formInput).toHaveAttribute('name', 'dateField') expect(formInput.value).toBe('2024-06-15') }) test('form input has no name attribute when name is not set', () => { const { container } = createDatepickerTest({ value: '2024-06-15', }) const formInput = container.querySelector('input[aria-hidden="true"]') as HTMLInputElement expect(formInput).not.toHaveAttribute('name') }) test('form input has comma-separated value for multiple', () => { const { container } = createDatepickerTest({ name: 'dates', multiple: true, value: ['2024-06-15', '2024-06-20'], }) const formInput = container.querySelector('input[aria-hidden="true"]') as HTMLInputElement expect(formInput.value).toBe('2024-06-15,2024-06-20') }) test('form input has comma-separated value for range', () => { const { container } = createDatepickerTest({ name: 'dateRange', range: true, value: ['2024-06-15', '2024-06-25'], }) const formInput = container.querySelector('input[aria-hidden="true"]') as HTMLInputElement expect(formInput.value).toBe('2024-06-15,2024-06-25') }) test('submits form on Enter key press', () => { const submitHandler = vi.fn((e: Event) => e.preventDefault()) const { container } = render(
, ) const input = container.querySelector('input[type="date"]') as HTMLInputElement fireEvent.keyDown(input, { key: 'Enter' }) expect(submitHandler).toHaveBeenCalled() }) }) })