Skip to content

Commit ad9d9c4

Browse files
committed
✨ feat(tests): add unit tests for DND components and enhance agenda utility functions
1 parent 3d22e6a commit ad9d9c4

File tree

7 files changed

+643
-2
lines changed

7 files changed

+643
-2
lines changed
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
import { useDndMonitor } from "@dnd-kit/core";
2+
import { renderHook } from "@testing-library/react";
3+
import { Categories_Event } from "@core/types/event.types";
4+
import dayjs from "@core/util/date/dayjs";
5+
import {
6+
ID_GRID_ALLDAY_ROW,
7+
ID_GRID_MAIN,
8+
} from "@web/common/constants/web.constants";
9+
import { editEventSlice } from "@web/ducks/events/slices/event.slice";
10+
import { useAppDispatch } from "@web/store/store.hooks";
11+
import { getSnappedMinutes } from "@web/views/Day/util/agenda/agenda.util";
12+
import { useEventDNDActions } from "../useEventDNDActions";
13+
14+
jest.mock("@dnd-kit/core", () => ({
15+
useDndMonitor: jest.fn(),
16+
}));
17+
18+
jest.mock("@web/store/store.hooks", () => ({
19+
useAppDispatch: jest.fn(),
20+
}));
21+
22+
jest.mock("@web/views/Day/util/agenda/agenda.util", () => ({
23+
getSnappedMinutes: jest.fn(),
24+
}));
25+
26+
describe("useEventDNDActions", () => {
27+
const mockDispatch = jest.fn();
28+
const mockEvent = {
29+
_id: "event-1",
30+
startDate: "2023-01-01T10:00:00.000Z",
31+
endDate: "2023-01-01T11:00:00.000Z",
32+
isAllDay: false,
33+
};
34+
35+
beforeEach(() => {
36+
jest.clearAllMocks();
37+
(useAppDispatch as jest.Mock).mockReturnValue(mockDispatch);
38+
});
39+
40+
it("should register dnd monitor", () => {
41+
renderHook(() => useEventDNDActions());
42+
expect(useDndMonitor).toHaveBeenCalledWith(
43+
expect.objectContaining({
44+
onDragEnd: expect.any(Function),
45+
}),
46+
);
47+
});
48+
49+
describe("onDragEnd", () => {
50+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
51+
let onDragEnd: (event: any) => void;
52+
53+
beforeEach(() => {
54+
renderHook(() => useEventDNDActions());
55+
onDragEnd = (useDndMonitor as jest.Mock).mock.calls[0][0].onDragEnd;
56+
});
57+
58+
it("should handle timed event move in main grid", () => {
59+
(getSnappedMinutes as jest.Mock).mockReturnValue(60); // Moved 1 hour
60+
61+
const active = {
62+
data: {
63+
current: {
64+
view: "day",
65+
type: Categories_Event.TIMED,
66+
event: mockEvent,
67+
},
68+
},
69+
};
70+
const over = { id: ID_GRID_MAIN };
71+
72+
onDragEnd({ active, over });
73+
74+
const expectedStartDate = dayjs(mockEvent.startDate)
75+
.startOf("day")
76+
.add(60, "minute")
77+
.toISOString();
78+
const expectedEndDate = dayjs(expectedStartDate)
79+
.add(60, "minute")
80+
.toISOString();
81+
82+
expect(mockDispatch).toHaveBeenCalledWith(
83+
editEventSlice.actions.request({
84+
_id: mockEvent._id,
85+
event: expect.objectContaining({
86+
startDate: expectedStartDate,
87+
endDate: expectedEndDate,
88+
}),
89+
}),
90+
);
91+
});
92+
93+
it("should handle all-day event move to main grid", () => {
94+
(getSnappedMinutes as jest.Mock).mockReturnValue(120); // Moved 2 hours
95+
96+
const allDayEvent = { ...mockEvent, isAllDay: true };
97+
const active = {
98+
data: {
99+
current: {
100+
view: "day",
101+
type: Categories_Event.ALLDAY,
102+
event: allDayEvent,
103+
},
104+
},
105+
};
106+
const over = { id: ID_GRID_MAIN };
107+
108+
onDragEnd({ active, over });
109+
110+
const expectedStartDate = dayjs(allDayEvent.startDate)
111+
.startOf("day")
112+
.add(120, "minute")
113+
.toISOString();
114+
const expectedEndDate = dayjs(expectedStartDate)
115+
.add(15, "minute")
116+
.toISOString();
117+
118+
expect(mockDispatch).toHaveBeenCalledWith(
119+
editEventSlice.actions.request({
120+
_id: allDayEvent._id,
121+
event: expect.objectContaining({
122+
isAllDay: false,
123+
startDate: expectedStartDate,
124+
endDate: expectedEndDate,
125+
}),
126+
}),
127+
);
128+
});
129+
130+
it("should handle timed event move to all-day grid", () => {
131+
const active = {
132+
data: {
133+
current: {
134+
view: "day",
135+
type: Categories_Event.TIMED,
136+
event: mockEvent,
137+
},
138+
},
139+
};
140+
const over = { id: ID_GRID_ALLDAY_ROW };
141+
142+
onDragEnd({ active, over });
143+
144+
const expectedStartDate = dayjs(mockEvent.startDate)
145+
.startOf("day")
146+
.format(dayjs.DateFormat.YEAR_MONTH_DAY_FORMAT);
147+
const expectedEndDate = dayjs(mockEvent.endDate)
148+
.startOf("day")
149+
.add(1, "day")
150+
.format(dayjs.DateFormat.YEAR_MONTH_DAY_FORMAT);
151+
152+
expect(mockDispatch).toHaveBeenCalledWith(
153+
editEventSlice.actions.request({
154+
_id: mockEvent._id,
155+
event: expect.objectContaining({
156+
isAllDay: true,
157+
startDate: expectedStartDate,
158+
endDate: expectedEndDate,
159+
}),
160+
}),
161+
);
162+
});
163+
164+
it("should ignore invalid drag end events", () => {
165+
const active = { data: { current: null } };
166+
const over = null;
167+
168+
onDragEnd({ active, over });
169+
170+
expect(mockDispatch).not.toHaveBeenCalled();
171+
});
172+
});
173+
});
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { render, screen } from "@testing-library/react";
2+
import { DNDContext } from "./DNDContext";
3+
4+
jest.mock("@dnd-kit/core", () => ({
5+
DndContext: jest.fn(({ children }) => (
6+
<div data-testid="dnd-context">{children}</div>
7+
)),
8+
KeyboardSensor: "KeyboardSensor",
9+
MouseSensor: "MouseSensor",
10+
TouchSensor: "TouchSensor",
11+
useSensor: jest.fn(),
12+
useSensors: jest.fn(),
13+
}));
14+
15+
describe("DNDContext", () => {
16+
it("renders children wrapped in DndContext", () => {
17+
render(
18+
<DNDContext>
19+
<div data-testid="child">Child</div>
20+
</DNDContext>,
21+
);
22+
23+
expect(screen.getByTestId("dnd-context")).toBeInTheDocument();
24+
expect(screen.getByTestId("child")).toBeInTheDocument();
25+
});
26+
});
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
import { useDndContext } from "@dnd-kit/core";
2+
import { render, screen } from "@testing-library/react";
3+
import { Categories_Event } from "@core/types/event.types";
4+
import { DNDOverlay } from "./DNDOverlay";
5+
6+
jest.mock("@dnd-kit/core", () => ({
7+
useDndContext: jest.fn(),
8+
DragOverlay: ({ children }: { children: React.ReactNode }) => (
9+
<div data-testid="drag-overlay">{children}</div>
10+
),
11+
}));
12+
13+
jest.mock("@dnd-kit/modifiers", () => ({
14+
restrictToVerticalAxis: jest.fn(),
15+
}));
16+
17+
jest.mock(
18+
"@web/views/Day/components/Agenda/Events/AgendaEvent/AgendaEvent",
19+
() => ({
20+
AgendaEvent: () => <div data-testid="agenda-event">Agenda Event</div>,
21+
}),
22+
);
23+
24+
jest.mock(
25+
"@web/views/Day/components/Agenda/Events/AllDayAgendaEvent/AllDayAgendaEvent",
26+
() => ({
27+
AllDayAgendaEvent: () => (
28+
<div data-testid="all-day-agenda-event">All Day Agenda Event</div>
29+
),
30+
}),
31+
);
32+
33+
describe("DNDOverlay", () => {
34+
beforeEach(() => {
35+
(useDndContext as jest.Mock).mockReturnValue({
36+
active: null,
37+
over: null,
38+
});
39+
});
40+
41+
it("renders nothing when no active item", () => {
42+
render(<DNDOverlay />);
43+
expect(screen.queryByTestId("drag-overlay")).toBeEmptyDOMElement();
44+
});
45+
46+
it("renders AgendaEvent when dragging TIMED event", () => {
47+
(useDndContext as jest.Mock).mockReturnValue({
48+
active: {
49+
id: "test-id",
50+
data: {
51+
current: {
52+
type: Categories_Event.TIMED,
53+
view: "day",
54+
event: { _id: "event-1" },
55+
},
56+
},
57+
},
58+
over: null,
59+
});
60+
61+
render(<DNDOverlay />);
62+
expect(screen.getByTestId("agenda-event")).toBeInTheDocument();
63+
});
64+
65+
it("renders AllDayAgendaEvent when dragging ALLDAY event", () => {
66+
(useDndContext as jest.Mock).mockReturnValue({
67+
active: {
68+
id: "test-id",
69+
data: {
70+
current: {
71+
type: Categories_Event.ALLDAY,
72+
view: "day",
73+
event: { _id: "event-1" },
74+
},
75+
},
76+
},
77+
over: null,
78+
});
79+
80+
render(<DNDOverlay />);
81+
expect(screen.getByTestId("all-day-agenda-event")).toBeInTheDocument();
82+
});
83+
84+
it("renders children for unknown types", () => {
85+
(useDndContext as jest.Mock).mockReturnValue({
86+
active: {
87+
id: "test-id",
88+
data: {
89+
current: {
90+
type: "UNKNOWN",
91+
view: "day",
92+
event: { _id: "event-1" },
93+
},
94+
},
95+
},
96+
over: null,
97+
});
98+
99+
render(
100+
<DNDOverlay>
101+
<div data-testid="child-content">Child Content</div>
102+
</DNDOverlay>,
103+
);
104+
expect(screen.getByTestId("child-content")).toBeInTheDocument();
105+
});
106+
});

0 commit comments

Comments
 (0)