import { InfoRow } from '@/components/Event/InfoRow'
import { LONG_DATE_FORMAT } from '@/components/Event/utils/dateTimeFormatters'
import * as eventThunks from '@/features/Calendars/services'
import EventUpdateModal from '@/features/Events/EventUpdateModal'
import { act, fireEvent, screen, waitFor } from '@testing-library/react'
import dayjs from 'dayjs'
import { renderWithProviders } from '../../utils/Renderwithproviders'
describe('Event Full Display', () => {
const mockOnClose = jest.fn()
const day = new Date('2025-01-15T10:00:00.000Z') // Fixed date in UTC: Jan 15, 2025 10:00 AM UTC
beforeEach(() => {
jest.clearAllMocks()
})
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: 'First Calendar',
color: '#FF0000',
events: {
event1: {
uid: 'event1',
title: 'Test Event',
calId: '667037022b752d0026472254/cal1',
start: day.toISOString(),
end: day.toISOString(),
organizer: { cn: 'test', cal_address: 'test@test.com' },
attendee: [
{
cn: 'test',
cal_address: 'test@test.com',
partstat: 'NEEDS-ACTION',
rsvp: 'TRUE',
role: 'REQ-PARTICIPANT',
cutype: 'INDIVIDUAL'
},
{
cn: 'John',
cal_address: 'john@test.com',
partstat: 'NEEDS-ACTION',
rsvp: 'TRUE',
role: 'REQ-PARTICIPANT',
cutype: 'INDIVIDUAL'
}
]
}
}
},
'otherCal/cal': {
id: 'otherCal/cal',
name: 'Calendar 1',
color: '#FF0000',
events: {
event1: {
uid: 'event1',
calId: 'otherCal/cal',
title: 'Test Event Other cal',
start: day.toISOString(),
end: day.toISOString(),
organizer: { cn: 'john', cal_address: 'john@test.com' }
}
}
}
}
}
}
it('renders correctly event data with fixed timezone', () => {
// Use fixed timezone UTC for consistent test results across all environments
const fixedDate = new Date('2025-01-15T10:00:00.000Z') // 10AM UTC
const endDate = new Date(fixedDate.getTime() + 3600000) // 11AM UTC
// With UTC timezone set, formatLocalDateTime produces predictable values
const expectedStart = '2025-01-15T10:00' // 10:00 AM UTC
const expectedEnd = '2025-01-15T11:00' // 11:00 AM UTC
const stateWithFixedDate = {
...preloadedState,
calendars: {
pending: false,
list: {
'667037022b752d0026472254/cal1': {
...preloadedState.calendars.list['667037022b752d0026472254/cal1'],
events: {
event1: {
...preloadedState.calendars.list[
'667037022b752d0026472254/cal1'
].events.event1,
start: fixedDate.toISOString(),
end: endDate.toISOString(),
timezone: 'UTC'
}
}
}
}
}
}
renderWithProviders(
,
stateWithFixedDate
)
expect(screen.getByDisplayValue('Test Event')).toBeInTheDocument()
// Expand to show date/time inputs (normal mode shows DateTimeSummary)
fireEvent.click(screen.getByRole('button', { name: 'common.moreOptions' }))
const startDateInput = screen.getByTestId('start-date-input')
const startTimeInput = screen.getByTestId('start-time-input')
const endTimeInput = screen.getByTestId('end-time-input')
expect(startDateInput).toBeInTheDocument()
expect(startTimeInput).toBeInTheDocument()
expect(endTimeInput).toBeInTheDocument()
expect(startDateInput).toHaveValue(
dayjs(expectedStart).format(LONG_DATE_FORMAT)
)
expect(startTimeInput).toHaveValue(dayjs(expectedStart).format('HH:mm'))
expect(endTimeInput).toHaveValue(dayjs(expectedEnd).format('HH:mm'))
expect(screen.getByText('First Calendar')).toBeInTheDocument()
})
it('calls onClose when Cancel clicked', () => {
renderWithProviders(
,
preloadedState
)
fireEvent.click(screen.getAllByTestId('CloseIcon')[0])
expect(mockOnClose).toHaveBeenCalledWith({}, 'backdropClick')
})
it('toggle Show More reveals extra fields', async () => {
renderWithProviders(
,
preloadedState
)
act(() => {
fireEvent.click(
screen.getByRole('button', { name: 'common.moreOptions' })
)
})
await waitFor(() => {
expect(screen.getByText('event.form.notification')).toBeInTheDocument()
})
// EventDisplay modal doesn't have Repeat checkbox, only RepeatEvent component
// which shows repetition settings when repetition data exists
// Since test event has no repetition data, RepeatEvent component won't show Repeat checkbox
fireEvent.click(screen.getByRole('button', { name: /Show Less/i }))
})
it('can edit title when user is organizer', () => {
renderWithProviders(
,
preloadedState
)
const titleField = screen.getByLabelText('event.form.title')
fireEvent.change(titleField, { target: { value: 'New Title' } })
expect(screen.getByDisplayValue('New Title')).toBeInTheDocument()
})
it('toggle all-day updates end date correctly', () => {
renderWithProviders(
,
preloadedState
)
fireEvent.click(screen.getByRole('button', { name: 'common.moreOptions' }))
const allDayCheckbox = screen.getByLabelText('event.form.allDay')
fireEvent.click(allDayCheckbox)
expect(allDayCheckbox).toBeChecked()
const expectedDate = dayjs(day).format(LONG_DATE_FORMAT)
const startDateInput = screen.getByTestId('start-date-input')
const endDateInput = screen.getByTestId('end-date-input')
expect(startDateInput).toHaveValue(expectedDate)
expect(endDateInput).toHaveValue(expectedDate)
})
it('saves event and moves it when calendar is changed', async () => {
const spyPut = jest
.spyOn(eventThunks, 'putEventAsync')
.mockImplementation(payload => {
const promise = Promise.resolve(payload)
;(promise as any).unwrap = () => promise
return () => promise as any
})
const spyMove = jest
.spyOn(eventThunks, 'moveEventAsync')
.mockImplementation(payload => {
const promise = Promise.resolve(payload)
;(promise as any).unwrap = () => promise
return () => promise as any
})
const testDate = new Date('2025-01-15T10:00:00.000Z')
const testEndDate = new Date('2025-01-15T11:00:00.000Z')
const preloadedTwoCals = {
...preloadedState,
calendars: {
pending: false,
list: {
'667037022b752d0026472254/cal1': {
id: '667037022b752d0026472254/cal1',
name: 'Calendar One',
color: '#FF0000',
events: {
event1: {
uid: 'event1',
title: 'Test Event',
calId: '667037022b752d0026472254/cal1',
start: testDate.toISOString(),
end: testEndDate.toISOString(),
organizer: { cn: 'test', cal_address: 'test@test.com' },
attendee: [{ cn: 'test', cal_address: 'test@test.com' }]
}
}
},
'667037022b752d0026472254/cal2': {
id: '667037022b752d0026472254/cal2',
name: 'Calendar Two',
color: '#00FF00',
events: {}
}
},
pending: false
}
}
await act(async () =>
renderWithProviders(
,
preloadedTwoCals
)
)
fireEvent.click(screen.getByRole('button', { name: 'common.moreOptions' }))
const calendarSelect = screen.getByLabelText('event.form.calendar')
await act(async () => fireEvent.mouseDown(calendarSelect))
const option = await screen.findByText('Calendar Two')
fireEvent.click(option)
await act(async () => {
fireEvent.click(screen.getByRole('button', { name: 'actions.save' }))
})
await waitFor(
() => {
expect(spyPut).toHaveBeenCalled()
},
{ timeout: 3000 }
)
await waitFor(
() => {
expect(spyMove).toHaveBeenCalled()
},
{ timeout: 3000 }
)
})
it('edit modal displays event time in original event timezone', () => {
// GIVEN user timezone is UTC+2
// WHEN the user edits an event at 2PM UTC+7 (Asia/Bangkok)
// THEN the update modal displays the time as 2PM in Asia/Bangkok timezone
const eventDateUTC7 = new Date('2025-01-15T07:00:00.000Z') // 7AM UTC = 2PM UTC+7
const stateWithTimezone = {
...preloadedState,
calendars: {
pending: false,
list: {
'667037022b752d0026472254/cal1': {
id: '667037022b752d0026472254/cal1',
name: 'Test Calendar',
color: '#FF0000',
events: {
event1: {
uid: 'event1',
title: 'Timezone Edit Test',
calId: '667037022b752d0026472254/cal1',
start: eventDateUTC7.toISOString(),
end: new Date(eventDateUTC7.getTime() + 3600000).toISOString(),
timezone: 'Asia/Bangkok',
allday: false,
organizer: { cn: 'test', cal_address: 'test@test.com' },
attendee: [{ cn: 'test', cal_address: 'test@test.com' }]
}
}
}
},
pending: false
}
}
renderWithProviders(
,
stateWithTimezone
)
// Expand to show timezone selector (normal mode hides it)
fireEvent.click(screen.getByRole('button', { name: 'common.moreOptions' }))
// The timezone select should have Asia/Bangkok selected
const timeZone = screen.getByDisplayValue(/Asia\/Bangkok/i)
expect(timeZone).toBeInTheDocument()
})
it('InfoRow renders error style when error prop is true', () => {
renderWithProviders(i} text="Bad" error />)
expect(screen.getByText('Bad')).toBeInTheDocument()
})
it('can remove an attendee with the close button', () => {
renderWithProviders(
,
preloadedState
)
const removeBtn = screen.getAllByTestId('CloseIcon').pop()!
fireEvent.click(removeBtn)
expect(screen.queryByText(/John/)).not.toBeInTheDocument()
})
it('renders video conference info when x_openpass_videoconference exists', () => {
const videoState = {
...preloadedState,
calendars: {
pending: false,
list: {
'667037022b752d0026472254/cal1': {
...preloadedState.calendars.list['667037022b752d0026472254/cal1'],
events: {
event1: {
...preloadedState.calendars.list[
'667037022b752d0026472254/cal1'
].events.event1,
x_openpass_videoconference: 'https://meet.test/video'
}
}
}
}
}
}
renderWithProviders(
,
videoState
)
expect(
screen.getByText('event.form.joinVisioConference')
).toBeInTheDocument()
})
})