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 = {}) => { await act(async () => render() ); }; 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(); }); }); });