Files
workavia-calendar-front/__test__/components/DateTimeField.test.tsx
T
2026-01-20 17:33:30 +01:00

344 lines
9.9 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();
});
});
});