import { CalendarApi } from '@fullcalendar/core' import { act, render, screen, fireEvent, waitFor, RenderResult } from '@testing-library/react' import React from 'react' import { DatePickerMobile } from '@/components/Menubar/components/DatePickerMobile' jest.mock('twake-i18n', () => ({ useI18n: (): { t: (key: string) => string; lang: string } => ({ t: (key: string) => key, lang: 'en' }) })) jest.mock('@linagora/twake-mui', () => { const actual = jest.requireActual( '@linagora/twake-mui' ) return { ...actual, useTheme: (): { palette: { grey: { 500: string } } } => ({ palette: { grey: { 500: '#9e9e9e' } } }) } }) const scrollIntoViewMock = jest.fn() // eslint-disable-next-line @typescript-eslint/unbound-method const originalScrollIntoView = window.HTMLElement.prototype.scrollIntoView beforeAll(() => { window.HTMLElement.prototype.scrollIntoView = scrollIntoViewMock }) afterAll(() => { window.HTMLElement.prototype.scrollIntoView = originalScrollIntoView }) function makeCalendarApi(initialDate: Date): CalendarApi & { getDate: jest.Mock gotoDate: jest.Mock } { let _date = initialDate const mockApi = { getDate: jest.fn(() => _date), gotoDate: jest.fn((d: Date) => { _date = d }) } return mockApi as unknown as CalendarApi & typeof mockApi } describe('DatePickerMobile', () => { beforeEach(() => { jest.clearAllMocks() scrollIntoViewMock.mockClear() }) const setup = async ( initialDate: Date ): Promise< RenderResult & { calendarApi: CalendarApi onDateChange: jest.Mock onCloseDatePicker: jest.Mock } > => { const calendarApi = makeCalendarApi(initialDate) const onDateChange = jest.fn() const onCloseDatePicker = jest.fn() const renderResult = await act(() => render( ) ) return { ...renderResult, calendarApi, onDateChange, onCloseDatePicker } } const expectNavigatedDate = ( calendarApi: CalendarApi, expected: { day: number; month: number; year: number } ): void => { // eslint-disable-next-line @typescript-eslint/unbound-method const gotoDateMock = calendarApi.gotoDate as jest.Mock const navigatedDate: Date = gotoDateMock.mock.calls[0][0] expect(navigatedDate.getDate()).toBe(expected.day) expect(navigatedDate.getMonth()).toBe(expected.month) expect(navigatedDate.getFullYear()).toBe(expected.year) } const testMonthNavigation = async ( initialDate: Date, monthToClick: number, expected: { day: number; month: number; year: number } ): Promise => { const { calendarApi } = await setup(initialDate) const monthButton = screen.getByText(`months.short.${monthToClick}`) act(() => { fireEvent.click(monthButton) }) await waitFor(() => { // eslint-disable-next-line @typescript-eslint/unbound-method expect(calendarApi.gotoDate).toHaveBeenCalled() }) expectNavigatedDate(calendarApi, expected) } it('calls calendarApi.gotoDate and onDateChange with the selected date', async () => { const initialDate = new Date(2025, 5, 15) // June 15, 2025 const { calendarApi, onDateChange } = await setup(initialDate) const day20 = screen.getByRole('gridcell', { name: '20' }) act(() => { fireEvent.click(day20) }) await waitFor(() => { // eslint-disable-next-line @typescript-eslint/unbound-method expect(calendarApi.gotoDate).toHaveBeenCalled() }) expectNavigatedDate(calendarApi, { day: 20, month: 5, year: 2025 }) // eslint-disable-next-line @typescript-eslint/unbound-method const gotoDateMock = calendarApi.gotoDate as jest.Mock expect(onDateChange).toHaveBeenCalledWith(gotoDateMock.mock.calls[0][0]) }) it('navigates to the clicked month while keeping the current day', async () => { await testMonthNavigation( new Date(2025, 5, 15), // June 15, 2025 8, { day: 15, month: 8, year: 2025 } ) }) it('clamps the day to the last valid day when switching to a shorter month', async () => { await testMonthNavigation( new Date(2025, 0, 31), // Jan 31, 2025 1, { day: 28, month: 1, year: 2025 } ) }) it('scrollIntoView is called on the correct month button when currentDate changes to another month', async () => { const initialDate = new Date(2025, 5, 15) // June 15, 2025 (month index 5) const { rerender, calendarApi, onDateChange } = await setup(initialDate) // Simulate parent updating currentDate to August (e.g. user picked a day in August) const newDate = new Date(2025, 7, 5) // August 5, 2025 (month index 7) act(() => { rerender( ) }) await waitFor(() => { // scrollIntoView should have been called at least once (initial mount + rerender) expect(scrollIntoViewMock).toHaveBeenCalledWith({ behavior: 'smooth', block: 'nearest', inline: 'center' }) }) // The last call should correspond to August (index 7) button being highlighted const augustButton = screen.getByText('months.short.7') expect(augustButton).toBeInTheDocument() }) })