import { RootState } from '@/app/store' import RepeatEvent from '@/components/Event/EventRepeat' import * as eventThunks from '@/features/Calendars/services' import EventPopover from '@/features/Events/EventModal' import { RepetitionObject } from '@/features/Events/EventsTypes' import { CalendarApi, DateSelectArg } from '@fullcalendar/core' import { act, fireEvent, screen, waitFor } from '@testing-library/react' import { renderWithProviders } from '../utils/Renderwithproviders' const baseRepetition: RepetitionObject = { freq: '', interval: 1, occurrences: 0, endDate: '' } const mockOnClose = jest.fn() const mockSetSelectedRange = jest.fn() const mockCalendarRef = { current: { select: jest.fn() } as unknown as CalendarApi } const preloadedState = { user: { userData: { sub: 'test', email: 'test@test.com', sid: 'aiYbWZSk2g0F+LrQeD7Dg4QcUMR8R/zTZdZBiA7N6Ro', openpaasId: '667037022b752d0026472254' }, organiserData: { cn: 'test', cal_address: 'test@test.com' } }, calendars: { list: { '667037022b752d0026472254/cal1': { id: '667037022b752d0026472254/cal1', name: 'Calendar 1', color: '#FF0000' }, '667037022b752d0026472254/cal2': { id: '667037022b752d0026472254/cal2', name: 'Calendar 2', color: '#00FF00' } }, pending: false } } const defaultSelectedRange = { startStr: '2025-07-18T09:00', endStr: '2025-07-18T10:00', start: new Date('2025-07-18T09:00'), end: new Date('2025-07-18T10:00'), allDay: false, resource: undefined } as unknown as DateSelectArg function setupRepeatEvent( props?: Partial, state?: RootState ) { const setRepetition = jest.fn() renderWithProviders( , state ) return { setRepetition } } async function setupEventPopover() { jest .spyOn(crypto, 'randomUUID') .mockReturnValue('fixed-uuid-with-correct-format') const originalDateResolvedOptions = new Intl.DateTimeFormat().resolvedOptions() jest.spyOn(Intl.DateTimeFormat.prototype, 'resolvedOptions').mockReturnValue({ ...originalDateResolvedOptions, timeZone: 'UTC' }) renderWithProviders( , preloadedState ) // Fill in title const titleInput = screen.getByLabelText('event.form.title') fireEvent.change(titleInput, { target: { value: 'Meeting' } }) // Click More options to expand the dialog const showMoreButton = screen.getByRole('button', { name: 'common.moreOptions' }) fireEvent.click(showMoreButton) // Check Repeat checkbox to show repeat options const repeatCheckbox = screen.getByLabelText('event.form.repeat') fireEvent.click(repeatCheckbox) // Wait for RepeatEvent component to be rendered await waitFor(() => { expect(screen.getByText('event.repeat.frequency.weeks')).toBeInTheDocument() }) } async function expectRRule(expected: Partial) { const spy = jest .spyOn(eventThunks, 'putEventAsync') .mockImplementation(payload => { const promise = Promise.resolve(payload) ;(promise as any).unwrap = () => promise return () => promise as any }) const saveButton = screen.getByRole('button', { name: /save/i }) act(() => fireEvent.click(saveButton)) await waitFor(() => expect(spy).toHaveBeenCalled()) const received = spy.mock.calls[0][0] expect(received.newEvent.repetition).toMatchObject(expected) } describe('RepeatEvent Component', () => { beforeEach(() => { jest.clearAllMocks() jest.restoreAllMocks() }) it('renders with no repetition by default', () => { setupRepeatEvent() // Check that interval input shows default value const intervalInput = screen.getByTestId('repeat-interval') expect(intervalInput).toBeInTheDocument() // Check that frequency dropdown shows default value const frequencySelect = screen.getByRole('combobox') expect(frequencySelect).toBeInTheDocument() }) it('allows selecting repetition frequency', () => { const { setRepetition } = setupRepeatEvent() // Click on frequency dropdown const frequencySelect = screen.getByRole('combobox') fireEvent.mouseDown(frequencySelect) // Select Week(s) const weeklyOption = screen.getByText('event.repeat.frequency.weeks') fireEvent.click(weeklyOption) expect(setRepetition).toHaveBeenCalledWith( expect.objectContaining({ freq: 'weekly' }) ) }) it('renders interval input when frequency is selected', () => { setupRepeatEvent({ freq: 'daily' }) const intervalInput = screen.getByTestId('repeat-interval') expect(intervalInput).toBeInTheDocument() }) it('updates interval value', () => { const { setRepetition } = setupRepeatEvent() const intervalInput = screen.getByTestId('repeat-interval') fireEvent.change(intervalInput, { target: { value: '3' } }) expect(setRepetition).toHaveBeenCalledWith( expect.objectContaining({ interval: 3 }) ) }) it('toggles day selection for weekly frequency', () => { const { setRepetition } = setupRepeatEvent({ freq: 'weekly' }) const mondayChip = screen.getByLabelText('event.repeat.days.monday') fireEvent.click(mondayChip) expect(setRepetition).toHaveBeenCalledWith( expect.objectContaining({ byday: ['MO'] }) ) }) }) describe('Repeat Event Integration Tests', () => { // Increase timeout for all tests in this describe block jest.setTimeout(30000) beforeEach(() => { jest.clearAllMocks() jest.restoreAllMocks() }) it('sends correct CalendarEvent payload for daily repeat', async () => { await setupEventPopover() // When Repeat checkbox is checked, repetition is set to empty object // We need to set the frequency manually const frequencySelect = screen.getByText('event.repeat.frequency.weeks') fireEvent.mouseDown(frequencySelect) const dailyOption = screen.getByRole('option', { name: 'event.repeat.frequency.days' }) fireEvent.click(dailyOption) await expectRRule({ freq: 'daily', interval: 1 }) expect(mockOnClose).toHaveBeenCalledWith(true) }) it('sends correct API payload for repeat daily with 2 day interval', async () => { await setupEventPopover() // Ensure frequency is daily const frequencySelect = screen.getByText('event.repeat.frequency.weeks') fireEvent.mouseDown(frequencySelect) const dailyOption = screen.getByRole('option', { name: 'event.repeat.frequency.days' }) fireEvent.click(dailyOption) // Set interval to 2 const intervalInput = screen.getByTestId('repeat-interval') fireEvent.change(intervalInput, { target: { value: '2' } }) await expectRRule({ freq: 'daily', interval: 2 }) expect(mockOnClose).toHaveBeenCalledWith(true) }) it('sends correct API payload for repeat daily for 5 repetitions', async () => { await setupEventPopover() // Ensure frequency is daily const frequencySelect = screen.getByText('event.repeat.frequency.weeks') fireEvent.mouseDown(frequencySelect) const dailyOption = screen.getByRole('option', { name: 'event.repeat.frequency.days' }) fireEvent.click(dailyOption) // Select "After" end option const afterRadio = screen.getByLabelText(/after/i) fireEvent.click(afterRadio) // Set occurrences to 5 const occurrencesInput = screen.getByTestId('occurrences-input') fireEvent.change(occurrencesInput, { target: { value: '5' } }) await expectRRule({ freq: 'daily', interval: 1, occurrences: 5 }) expect(mockOnClose).toHaveBeenCalledWith(true) }) it('sends correct API payload for repeat daily until specific date', async () => { await setupEventPopover() // Ensure frequency is daily const frequencySelect = screen.getByText('event.repeat.frequency.weeks') fireEvent.mouseDown(frequencySelect) const dailyOption = screen.getByRole('option', { name: 'event.repeat.frequency.days' }) fireEvent.click(dailyOption) // Select "On" end option const onRadio = screen .getAllByLabelText(/on/i) .find(el => el.type === 'radio') fireEvent.click(onRadio!) // End date is set by UI to some valid date string (YYYY-MM-DD) await expectRRule({ freq: 'daily', interval: 1, endDate: expect.any(String) }) expect(mockOnClose).toHaveBeenCalledWith(true) }) it('sends correct API payload for repeat weekly on specific days', async () => { await setupEventPopover() // Select Week(s) frequency const frequencySelect = screen.getByText('event.repeat.frequency.weeks') fireEvent.mouseDown(frequencySelect) const weeklyOption = screen.getByRole('option', { name: 'event.repeat.frequency.weeks' }) fireEvent.click(weeklyOption) // Select Thursday const thursdayCheckbox = screen.getByLabelText('event.repeat.days.thursday') fireEvent.click(thursdayCheckbox) await expectRRule({ freq: 'weekly', interval: 1, byday: ['FR', 'TH'] }) expect(mockOnClose).toHaveBeenCalledWith(true) }) it('sends correct API payload for repeat weekly with 3 week interval', async () => { await setupEventPopover() // Select Week(s) frequency const frequencySelect = screen.getByText('event.repeat.frequency.weeks') fireEvent.mouseDown(frequencySelect) const weeklyOption = screen.getByRole('option', { name: 'event.repeat.frequency.weeks' }) fireEvent.click(weeklyOption) // Set interval to 3 const intervalInput = screen.getByTestId('repeat-interval') fireEvent.change(intervalInput, { target: { value: '3' } }) await expectRRule({ freq: 'weekly', interval: 3 }) expect(mockOnClose).toHaveBeenCalledWith(true) }) it('sends correct API payload for repeat monthly', async () => { await setupEventPopover() // Select Month(s) frequency const frequencySelect = screen.getByText('event.repeat.frequency.weeks') fireEvent.mouseDown(frequencySelect) const monthlyOption = screen.getByRole('option', { name: 'event.repeat.frequency.months' }) fireEvent.click(monthlyOption) await expectRRule({ freq: 'monthly', interval: 1 }) expect(mockOnClose).toHaveBeenCalledWith(true) }) it('sends correct API payload for repeat monthly and end after 5 occurrences', async () => { await setupEventPopover() // Select Month(s) frequency const frequencySelect = screen.getByText('event.repeat.frequency.weeks') fireEvent.mouseDown(frequencySelect) const monthlyOption = screen.getByRole('option', { name: 'event.repeat.frequency.months' }) fireEvent.click(monthlyOption) // Select "After" end option const afterRadio = screen.getByLabelText(/after/i) fireEvent.click(afterRadio) // Set occurrences to 5 const occurrencesInput = screen.getByTestId('occurrences-input') fireEvent.change(occurrencesInput, { target: { value: '5' } }) await expectRRule({ freq: 'monthly', interval: 1, occurrences: 5 }) expect(mockOnClose).toHaveBeenCalledWith(true) }) it('sends correct API payload for repeat yearly', async () => { await setupEventPopover() // Select Year(s) frequency const frequencySelect = screen.getByText('event.repeat.frequency.weeks') fireEvent.mouseDown(frequencySelect) const yearlyOption = screen.getByRole('option', { name: 'event.repeat.frequency.years' }) fireEvent.click(yearlyOption) await expectRRule({ freq: 'yearly', interval: 1 }) expect(mockOnClose).toHaveBeenCalledWith(true) }) it('sends correct API payload for repeat yearly with end option changes', async () => { await setupEventPopover() // Select Year(s) frequency const frequencySelect = screen.getByText('event.repeat.frequency.weeks') fireEvent.mouseDown(frequencySelect) const yearlyOption = screen.getByRole('option', { name: 'event.repeat.frequency.years' }) fireEvent.click(yearlyOption) // First choose "After" with 5 occurrences const afterRadio = screen.getByLabelText(/after/i) fireEvent.click(afterRadio) const occurrencesInput = screen.getByTestId('occurrences-input') fireEvent.change(occurrencesInput, { target: { value: '5' } }) // Then change mind and choose "Never" const neverRadio = screen.getByLabelText(/never/i) fireEvent.click(neverRadio) await expectRRule({ freq: 'yearly', interval: 1 }) expect(mockOnClose).toHaveBeenCalledWith(true) }) })