cadfa70e60
* #708 apply strictier linting rules and fix simple eslint bugs * #708 fix eslint errors relate to promise * #708 fix eslint import/no-extraneous-dependencies * #708 fix eslint errors of react-hook * #708 enable eslint check for typescript --------- Co-authored-by: lethemanh <lethemanh@lethemanhs-MacBook-Pro.local>
332 lines
9.8 KiB
TypeScript
332 lines
9.8 KiB
TypeScript
import {
|
|
DateTimeFields,
|
|
DateTimeFieldsProps
|
|
} from '@/components/Event/components/DateTimeFields'
|
|
import { act, fireEvent, render, screen, waitFor } from '@testing-library/react'
|
|
import userEvent from '@testing-library/user-event'
|
|
|
|
jest.mock('twake-i18n', () => ({
|
|
useI18n: () => ({
|
|
t: (key: string) => key,
|
|
lang: 'en'
|
|
})
|
|
}))
|
|
|
|
describe('DateTimeFields', () => {
|
|
const mockHandlers = {
|
|
onStartDateChange: jest.fn(),
|
|
onStartTimeChange: jest.fn(),
|
|
onEndDateChange: jest.fn(),
|
|
onEndTimeChange: jest.fn(),
|
|
onToggleEndDate: jest.fn()
|
|
}
|
|
|
|
const defaultProps: DateTimeFieldsProps = {
|
|
startDate: '2025-07-18',
|
|
startTime: '09:00',
|
|
endDate: '2025-07-18',
|
|
endTime: '10:00',
|
|
allday: false,
|
|
showMore: true,
|
|
hasEndDateChanged: false,
|
|
showEndDate: false,
|
|
validation: {
|
|
errors: {
|
|
dateTime: ''
|
|
}
|
|
},
|
|
...mockHandlers
|
|
}
|
|
|
|
const renderField = async (props: Partial<DateTimeFieldsProps> = {}) => {
|
|
await act(async () =>
|
|
render(<DateTimeFields {...defaultProps} {...props} />)
|
|
)
|
|
}
|
|
|
|
beforeEach(() => {
|
|
jest.clearAllMocks()
|
|
})
|
|
|
|
it('moves END forward when START moves after END (normal mode)', async () => {
|
|
await renderField({
|
|
startDate: '2025-01-01',
|
|
startTime: '10:00',
|
|
endDate: '2025-01-01',
|
|
endTime: '11:00',
|
|
showMore: true
|
|
})
|
|
|
|
const startTimeInput = screen.getByTestId('start-time-input')
|
|
|
|
fireEvent.change(startTimeInput, { target: { value: '12:00' } })
|
|
fireEvent.blur(startTimeInput)
|
|
|
|
await waitFor(() =>
|
|
expect(mockHandlers.onStartTimeChange).toHaveBeenCalledWith('12:00')
|
|
)
|
|
await waitFor(() =>
|
|
expect(mockHandlers.onEndTimeChange).toHaveBeenCalledWith('13:00')
|
|
)
|
|
})
|
|
|
|
it('moves END forward by full duration when START date jumps after END date', async () => {
|
|
await renderField({
|
|
startDate: '2025-01-01',
|
|
startTime: '09:00',
|
|
endDate: '2025-01-01',
|
|
endTime: '10:00',
|
|
showMore: true
|
|
})
|
|
|
|
await userEvent.click(screen.getByTestId('start-date-input'))
|
|
const dayButton = screen.getByRole('gridcell', { name: '3' })
|
|
await userEvent.click(dayButton)
|
|
|
|
await waitFor(() =>
|
|
expect(mockHandlers.onStartDateChange).toHaveBeenCalledWith('2025-01-03')
|
|
)
|
|
|
|
await waitFor(() =>
|
|
expect(mockHandlers.onEndDateChange).toHaveBeenCalledWith('2025-01-03')
|
|
)
|
|
})
|
|
|
|
it('does NOT move START backward when END moves before START (normal mode)', async () => {
|
|
await renderField({
|
|
startDate: '2025-01-01',
|
|
startTime: '10:00',
|
|
endDate: '2025-01-01',
|
|
endTime: '11:00',
|
|
showMore: true
|
|
})
|
|
|
|
const endTimeInput = screen.getByTestId('end-time-input')
|
|
|
|
fireEvent.change(endTimeInput, { target: { value: '08:00' } })
|
|
fireEvent.blur(endTimeInput)
|
|
|
|
await waitFor(() =>
|
|
expect(mockHandlers.onEndTimeChange).toHaveBeenCalledWith('08:00')
|
|
)
|
|
|
|
// Start time should NOT be automatically adjusted when end time changes
|
|
expect(mockHandlers.onStartTimeChange).not.toHaveBeenCalled()
|
|
expect(mockHandlers.onStartDateChange).not.toHaveBeenCalled()
|
|
})
|
|
|
|
it('moves START backward properly when END date jumps before START date', async () => {
|
|
await renderField({
|
|
startDate: '2025-01-05',
|
|
startTime: '09:00',
|
|
endDate: '2025-01-05',
|
|
endTime: '10:00',
|
|
showMore: true
|
|
})
|
|
|
|
await userEvent.click(screen.getByTestId('end-date-input'))
|
|
const dayButton = screen.getByRole('gridcell', { name: '3' })
|
|
await userEvent.click(dayButton)
|
|
|
|
await waitFor(() =>
|
|
expect(mockHandlers.onEndDateChange).toHaveBeenCalledWith('2025-01-03')
|
|
)
|
|
|
|
await waitFor(() =>
|
|
expect(mockHandlers.onStartDateChange).toHaveBeenCalledWith('2025-01-03')
|
|
)
|
|
})
|
|
|
|
it('pushes END forward in whole days for allday events', async () => {
|
|
await renderField({
|
|
allday: true,
|
|
startDate: '2025-02-01',
|
|
endDate: '2025-02-03',
|
|
showEndDate: true
|
|
})
|
|
|
|
await userEvent.click(screen.getByTestId('start-date-input'))
|
|
const dayButton = screen.getByRole('gridcell', { name: '10' })
|
|
await userEvent.click(dayButton)
|
|
|
|
await waitFor(() =>
|
|
expect(mockHandlers.onStartDateChange).toHaveBeenCalledWith('2025-02-10')
|
|
)
|
|
|
|
await waitFor(() =>
|
|
expect(mockHandlers.onEndDateChange).toHaveBeenCalledWith('2025-02-12')
|
|
)
|
|
})
|
|
|
|
it('moves START backward in whole days for allday when END moves earlier', async () => {
|
|
await renderField({
|
|
allday: true,
|
|
startDate: '2025-05-10',
|
|
endDate: '2025-05-15',
|
|
showEndDate: true
|
|
})
|
|
|
|
await userEvent.click(screen.getByTestId('end-date-input'))
|
|
const dayButton = screen.getByRole('gridcell', { name: '1' })
|
|
await userEvent.click(dayButton)
|
|
|
|
await waitFor(() =>
|
|
expect(mockHandlers.onEndDateChange).toHaveBeenCalledWith('2025-05-01')
|
|
)
|
|
|
|
await waitFor(() =>
|
|
expect(mockHandlers.onStartDateChange).toHaveBeenCalledWith('2025-04-26')
|
|
)
|
|
})
|
|
|
|
it('does not call handlers if invalid (null) date value', async () => {
|
|
await renderField({
|
|
showMore: true
|
|
})
|
|
|
|
const startDateInput = screen.getByTestId('start-date-input')
|
|
|
|
fireEvent.change(startDateInput, { target: { value: '' } })
|
|
|
|
expect(mockHandlers.onStartDateChange).not.toHaveBeenCalled()
|
|
expect(mockHandlers.onEndDateChange).not.toHaveBeenCalled()
|
|
})
|
|
|
|
it('shift to preserve original duration (normal case)', async () => {
|
|
await renderField({
|
|
startDate: '2025-01-01',
|
|
startTime: '09:00',
|
|
endDate: '2025-01-01',
|
|
endTime: '10:00',
|
|
showMore: true
|
|
})
|
|
|
|
const startTimeInput = screen.getByTestId('start-time-input')
|
|
|
|
fireEvent.change(startTimeInput, { target: { value: '09:30' } })
|
|
fireEvent.blur(startTimeInput)
|
|
|
|
await waitFor(() =>
|
|
expect(mockHandlers.onStartTimeChange).toHaveBeenCalledWith('09:30')
|
|
)
|
|
await waitFor(() =>
|
|
expect(mockHandlers.onEndTimeChange).toHaveBeenCalledWith('10:30')
|
|
)
|
|
})
|
|
|
|
it('preserves 1-hour duration across midnight when changing start time from 22:30 to 23:45', async () => {
|
|
await renderField({
|
|
startDate: '2025-01-15',
|
|
startTime: '22:30',
|
|
endDate: '2025-01-15',
|
|
endTime: '23:30',
|
|
showMore: true
|
|
})
|
|
|
|
const startTimeInput = screen.getByTestId('start-time-input')
|
|
|
|
// Change start time from 22:30 to 23:45
|
|
fireEvent.change(startTimeInput, { target: { value: '23:45' } })
|
|
fireEvent.blur(startTimeInput)
|
|
|
|
await waitFor(() =>
|
|
expect(mockHandlers.onStartTimeChange).toHaveBeenCalledWith('23:45')
|
|
)
|
|
|
|
// End date should move to the next day
|
|
await waitFor(() =>
|
|
expect(mockHandlers.onEndDateChange).toHaveBeenCalledWith(
|
|
'2025-01-16',
|
|
'00:45'
|
|
)
|
|
)
|
|
})
|
|
|
|
it('should have aria-label for accessibility and testing', async () => {
|
|
await renderField({
|
|
startDate: '2025-01-01',
|
|
startTime: '10:00',
|
|
endDate: '2025-01-01',
|
|
endTime: '11:00',
|
|
showMore: true
|
|
})
|
|
|
|
const startDateInput = screen.getByTestId('start-date-input')
|
|
const startTimeInput = screen.getByTestId('start-time-input')
|
|
const endDateInput = screen.getByTestId('end-date-input')
|
|
const endTimeInput = screen.getByTestId('end-time-input')
|
|
|
|
expect(startDateInput).toHaveAttribute(
|
|
'aria-label',
|
|
'dateTimeFields.startDate'
|
|
)
|
|
expect(startTimeInput).toHaveAttribute(
|
|
'aria-label',
|
|
'dateTimeFields.startTime'
|
|
)
|
|
expect(endDateInput).toHaveAttribute('aria-label', 'dateTimeFields.endDate')
|
|
expect(endTimeInput).toHaveAttribute('aria-label', 'dateTimeFields.endTime')
|
|
})
|
|
|
|
describe('DateTimeFields - Drag and Drop Display Logic', () => {
|
|
// Test 1.1: Display 2 fields when drag from allday slot (multiple days)
|
|
it('displays only 2 date fields when allday=true and multiple days', async () => {
|
|
await renderField({
|
|
allday: true,
|
|
hasEndDateChanged: false,
|
|
startDate: '2025-07-18',
|
|
endDate: '2025-07-20',
|
|
showEndDate: true,
|
|
showMore: false
|
|
})
|
|
|
|
expect(screen.getByTestId('start-date-input')).toBeInTheDocument()
|
|
expect(screen.getByTestId('end-date-input')).toBeInTheDocument()
|
|
expect(screen.queryByTestId('start-time-input')).not.toBeInTheDocument()
|
|
expect(screen.queryByTestId('end-time-input')).not.toBeInTheDocument()
|
|
})
|
|
|
|
// Test 1.2: Display 4 fields when drag from week view (multiple days)
|
|
it('displays 4 fields when allday=false, hasEndDateChanged=true, and multiple days', async () => {
|
|
await renderField({
|
|
allday: false,
|
|
hasEndDateChanged: true,
|
|
startDate: '2025-07-18',
|
|
endDate: '2025-07-20',
|
|
startTime: '09:00',
|
|
endTime: '10:00',
|
|
showEndDate: true,
|
|
showMore: false
|
|
})
|
|
|
|
expect(screen.getByTestId('start-date-input')).toBeInTheDocument()
|
|
expect(screen.getByTestId('start-time-input')).toBeInTheDocument()
|
|
expect(screen.getByTestId('end-date-input')).toBeInTheDocument()
|
|
expect(screen.getByTestId('end-time-input')).toBeInTheDocument()
|
|
})
|
|
|
|
// Test 1.3: Display single date + time fields
|
|
it('displays single date field with time fields for single day event', async () => {
|
|
await renderField({
|
|
allday: false,
|
|
hasEndDateChanged: false,
|
|
startDate: '2025-07-18',
|
|
endDate: '2025-07-18',
|
|
startTime: '09:00',
|
|
endTime: '10:00',
|
|
showEndDate: false,
|
|
showMore: false
|
|
})
|
|
|
|
const startDateInput = screen.getByTestId('start-date-input')
|
|
expect(startDateInput).toHaveAttribute(
|
|
'aria-label',
|
|
'dateTimeFields.date'
|
|
)
|
|
expect(screen.getByTestId('start-time-input')).toBeInTheDocument()
|
|
expect(screen.getByTestId('end-time-input')).toBeInTheDocument()
|
|
expect(screen.queryByTestId('end-date-input')).not.toBeInTheDocument()
|
|
})
|
|
})
|
|
})
|