import React from 'react'; import type { i18n } from 'i18next'; import { beforeEach, describe, expect, it, vi } from 'vitest'; import '@testing-library/jest-dom/vitest'; import { render, screen, within } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { useConfig } from '@openmrs/esm-react-utils/mock'; import { OpenmrsDatePicker } from './index'; import { DEFAULT_MIN_DATE_FLOOR } from './defaults'; window.i18next = { language: 'en' } as i18n; describe('OpenmrsDatePicker', () => { beforeEach(() => { useConfig.mockReturnValue({ preferredDateLocale: { en: 'en-GB', }, }); }); describe('locale and format', () => { it('uses dd/mm/yyyy for english by default', () => { render(); const input = screen.getByLabelText('datepicker'); expect(input).toHaveTextContent('dd/mm/yyyy'); }); it('should respect the preferred date locale', () => { useConfig.mockReturnValue({ preferredDateLocale: { en: 'en-US', }, }); render(); const input = screen.getByLabelText('datepicker'); expect(input).toHaveTextContent('mm/dd/yyyy'); }); it('should render RTL layout for Arabic locale', () => { window.i18next = { language: 'ar' } as i18n; useConfig.mockReturnValue({ preferredDateLocale: {} }); render(); const input = screen.getByLabelText('datepicker'); const text = input.textContent?.replace(/\u200F/g, ''); expect(text).toBe('يوم/شهر/سنة'); window.i18next = { language: 'en' } as i18n; }); it('should render RTL layout for Amharic locale', () => { window.i18next = { language: 'am' } as i18n; useConfig.mockReturnValue({ preferredDateLocale: {} }); render(); const input = screen.getByLabelText('datepicker'); const text = input.textContent?.replace(/\u200F/g, ''); expect(text).toBe('ቀቀ/ሚሜ/ዓዓዓዓ'); window.i18next = { language: 'en' } as i18n; }); }); describe('labels and accessibility', () => { it('should work with aria-label when labelText is empty', () => { render(); const group = screen.getByRole('group', { name: /Select appointment date/i }); expect(group).toBeInTheDocument(); expect(screen.queryByText('Select appointment date')).not.toBeInTheDocument(); }); it('should render visible label when labelText is provided', () => { render(); const labelText = screen.getByText('Appointment date'); expect(labelText).toBeInTheDocument(); expect(labelText).toHaveClass('cds--label'); const group = screen.getByRole('group'); expect(group).toBeInTheDocument(); }); it('should warn in development when neither labelText nor aria-label is provided', () => { const consoleWarnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {}); render(); expect(consoleWarnSpy).toHaveBeenCalledWith( 'OpenmrsDatePicker: You must provide either a visible label (labelText/label) or an aria-label for accessibility.', ); consoleWarnSpy.mockRestore(); }); }); describe('value display', () => { it('should display a prefilled date from the value prop', () => { render(); const input = screen.getByLabelText('datepicker'); expect(input).toHaveTextContent('15/03/2025'); }); it('should display a prefilled date from the defaultValue prop', () => { render(); const input = screen.getByLabelText('datepicker'); expect(input).toHaveTextContent('18/06/2025'); }); }); describe('invalid state', () => { it('should display invalidText when invalid is true', () => { render(); expect(screen.getByText('Date is required')).toBeInTheDocument(); }); it('should not display invalidText when invalid is false', () => { render(); expect(screen.queryByText('Date is required')).not.toBeInTheDocument(); }); it('should display invalidText when isInvalid is true', () => { render(); expect(screen.getByText('Bad date')).toBeInTheDocument(); }); }); describe('disabled state', () => { it('should render disabled label styling when isDisabled is true', () => { render(); const label = screen.getByText('Date'); expect(label).toHaveClass('cds--label--disabled'); }); }); describe('onChange callbacks', () => { it('should log an error when both onChange and onChangeRaw are provided', () => { const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {}); const onChange = vi.fn(); const onChangeRaw = vi.fn(); render(); expect(consoleErrorSpy).toHaveBeenCalledWith( 'An OpenmrsDatePicker component was created with both onChange and onChangeRaw handlers defined. Only onChangeRaw will be used.', ); consoleErrorSpy.mockRestore(); }); }); describe('calendar popover', () => { it('should open the calendar popover when the calendar button is clicked', async () => { const user = userEvent.setup(); render(); const button = screen.getByRole('button'); await user.click(button); expect(screen.getByRole('dialog')).toBeInTheDocument(); expect(screen.getByRole('grid')).toBeInTheDocument(); }); it('should clamp previous-month navigation at the default minDate floor when minDate is omitted', async () => { const user = userEvent.setup(); render( , ); await user.click(screen.getByRole('button')); const dialog = screen.getByRole('dialog'); const previousButton = within(dialog).getByRole('button', { name: /previous/i }); expect(previousButton).toBeInTheDocument(); expect(previousButton).toBeDisabled(); }); }); });