Skip to content

Commit 41a3cee

Browse files
committed
feat(tests): enhance unit tests for EventPopup, FloatButtonSection, Toastify, and CalendarIntro components
1 parent 336c4ac commit 41a3cee

12 files changed

Lines changed: 1138 additions & 734 deletions

.husky/pre-commit

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
# npm run lint
22
npm run format:check
3-
# npm test
3+
npm test

src/__tests__/App.test.jsx

Lines changed: 139 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -1,97 +1,164 @@
11
import React from "react";
2-
import { render, screen } from "@testing-library/react";
2+
import { render, screen, fireEvent, waitFor } from "@testing-library/react";
33
import App from "../App";
4+
import { ThemeContext } from "../store/Theme/ThemeContext";
45

5-
// Mock child components
6-
jest.mock("../components/Header", () => {
7-
return function DummyHeader() {
8-
return <div data-testid="header">Header</div>;
6+
jest.mock("antd", () => {
7+
const React = require("react");
8+
const theme = {
9+
defaultAlgorithm: "defaultAlgorithm",
10+
darkAlgorithm: "darkAlgorithm",
911
};
10-
});
1112

12-
jest.mock("../components/Footer", () => {
13-
return function DummyFooter() {
14-
return <div data-testid="footer">Footer</div>;
15-
};
13+
const ConfigProvider = ({ children, theme: themeProp }) => (
14+
<div
15+
data-testid="config-provider"
16+
data-theme={JSON.stringify(themeProp)}
17+
>
18+
{children}
19+
</div>
20+
);
21+
22+
return { ConfigProvider, theme };
1623
});
1724

25+
const mockCsCalendar = jest.fn();
26+
let mockAnnouncementProps = null;
27+
1828
jest.mock("../components/CSCalendar", () => {
19-
return function DummyCSCalendar() {
20-
return <div data-testid="calendar">Calendar</div>;
29+
const React = require("react");
30+
return function MockCSCalendar(props) {
31+
mockCsCalendar(props);
32+
React.useEffect(() => {
33+
props.setAnnouncementData({
34+
startWeekDate: "2025/01/13",
35+
endWeekDate: "2025/01/20",
36+
firstEventDate: "2025/01/15",
37+
secondEventDate: "2025/01/19",
38+
firstEvent: "First",
39+
secondEvent: "Second",
40+
});
41+
}, [props.setAnnouncementData]);
42+
return <div data-testid="calendar" />;
2143
};
2244
});
2345

46+
jest.mock("../components/Footer", () => () => (
47+
<div data-testid="footer">footer</div>
48+
));
49+
2450
jest.mock("../components/FloatButtonSection", () => {
25-
return function DummyFloatButtonSection() {
26-
return <div data-testid="float-button">Float Button</div>;
51+
return function MockFloatButtonSection({ setIsModalOpen }) {
52+
return (
53+
<button
54+
data-testid="float-toggle"
55+
onClick={() => setIsModalOpen(true)}
56+
>
57+
toggle
58+
</button>
59+
);
2760
};
2861
});
2962

3063
jest.mock("../components/AnnouncementModule", () => {
31-
return function DummyAnnouncementModule() {
32-
return <div data-testid="announcement">Announcement</div>;
64+
return function MockAnnouncementModule(props) {
65+
mockAnnouncementProps = props;
66+
return (
67+
<div
68+
data-testid="announcement"
69+
onClick={() => props.setAddToCurrentWeek((prev) => prev + 1)}
70+
>
71+
announcement
72+
</div>
73+
);
3374
};
3475
});
3576

3677
jest.mock("../components/Toastify", () => {
37-
return function DummyToastify() {
38-
return <div data-testid="toastify">Toastify</div>;
78+
return function MockToastify({ toastifyObj }) {
79+
return (
80+
<div data-testid="toastify" data-mode={toastifyObj?.mode || ""} />
81+
);
3982
};
4083
});
4184

42-
jest.mock("../store/StoreProvider", () => {
43-
return function DummyStoreProvider({ children }) {
44-
return <div data-testid="store-provider">{children}</div>;
45-
};
46-
});
85+
describe("App", () => {
86+
beforeEach(() => {
87+
document.body.className = "";
88+
jest.useFakeTimers();
89+
jest.clearAllMocks();
90+
mockAnnouncementProps = null;
91+
mockCsCalendar.mockClear();
92+
});
4793

48-
// Mock ThemeContext
49-
jest.mock("../store/Theme/ThemeContext", () => {
50-
const React = require("react");
51-
const mockThemeContext = {
52-
theme: "light",
53-
toggleTheme: jest.fn(),
54-
};
55-
return {
56-
__esModule: true,
57-
default: React.createContext(mockThemeContext),
58-
ThemeContext: React.createContext(mockThemeContext),
59-
};
60-
});
94+
afterEach(() => {
95+
jest.useRealTimers();
96+
});
97+
98+
const renderWithTheme = (themeValue = "light") =>
99+
render(
100+
<ThemeContext.Provider
101+
value={{ theme: themeValue, toggleTheme: jest.fn() }}
102+
>
103+
<App />
104+
</ThemeContext.Provider>
105+
);
106+
107+
it("applies the correct theme algorithm and mounts children for light mode", () => {
108+
renderWithTheme("light");
109+
jest.runAllTimers();
110+
111+
const providers = screen.getAllByTestId("config-provider");
112+
expect(providers.length).toBeGreaterThan(0);
113+
const outerTheme = JSON.parse(providers[0].dataset.theme);
114+
expect(outerTheme.algorithm).toBe("defaultAlgorithm");
115+
expect(screen.getByTestId("calendar")).toBeInTheDocument();
116+
expect(screen.getByTestId("footer")).toBeInTheDocument();
117+
});
118+
119+
it("switches to dark algorithm when theme context is dark", () => {
120+
renderWithTheme("dark");
121+
jest.runAllTimers();
122+
123+
const providers = screen.getAllByTestId("config-provider");
124+
const outerTheme = JSON.parse(providers[0].dataset.theme);
125+
expect(outerTheme.algorithm).toBe("darkAlgorithm");
126+
});
61127

62-
// describe("App", () => {
63-
// it("should render without crashing", () => {
64-
// render(<App />);
65-
// });
66-
67-
// // TEMPORARILY DISABLED
68-
// // it("should render Header component", () => {
69-
// // render(<App />);
70-
// // expect(screen.getByTestId("header")).toBeInTheDocument();
71-
// // });
72-
73-
// it("should render Footer component", () => {
74-
// render(<App />);
75-
// expect(screen.getByTestId("footer")).toBeInTheDocument();
76-
// });
77-
78-
// it("should render CSCalendar component", () => {
79-
// render(<App />);
80-
// expect(screen.getByTestId("calendar")).toBeInTheDocument();
81-
// });
82-
83-
// it("should render FloatButtonSection component", () => {
84-
// render(<App />);
85-
// expect(screen.getByTestId("float-button")).toBeInTheDocument();
86-
// });
87-
88-
// it("should render AnnouncementModule component", () => {
89-
// render(<App />);
90-
// expect(screen.getByTestId("announcement")).toBeInTheDocument();
91-
// });
92-
93-
// it("should render Toastify component", () => {
94-
// render(<App />);
95-
// expect(screen.getByTestId("toastify")).toBeInTheDocument();
96-
// });
97-
// });
128+
it("adds the loaded class to body after the initial effect", () => {
129+
renderWithTheme();
130+
expect(document.body.classList.contains("loaded")).toBe(false);
131+
jest.runAllTimers();
132+
expect(document.body.classList.contains("loaded")).toBe(true);
133+
});
134+
135+
it("propagates announcement data from calendar to announcement module", async () => {
136+
renderWithTheme();
137+
await waitFor(() =>
138+
expect(mockAnnouncementProps?.announcementData?.firstEvent).toBe(
139+
"First"
140+
)
141+
);
142+
expect(mockCsCalendar).toHaveBeenCalled();
143+
});
144+
145+
it("updates addToCurrentWeek state when announcement module requests it", async () => {
146+
renderWithTheme();
147+
fireEvent.click(screen.getByTestId("announcement"));
148+
149+
await waitFor(() => {
150+
const lastCall =
151+
mockCsCalendar.mock.calls[mockCsCalendar.mock.calls.length - 1];
152+
expect(lastCall[0].addToCurrentWeek).toBe(1);
153+
});
154+
});
155+
156+
it("opens the announcement modal via float button toggle", async () => {
157+
renderWithTheme();
158+
fireEvent.click(screen.getByTestId("float-toggle"));
159+
160+
await waitFor(() =>
161+
expect(mockAnnouncementProps?.isModalOpen).toBe(true)
162+
);
163+
});
164+
});

0 commit comments

Comments
 (0)