|
1 | 1 | import React from "react"; |
2 | | -import { render, screen } from "@testing-library/react"; |
| 2 | +import { render, screen, fireEvent, waitFor } from "@testing-library/react"; |
3 | 3 | import App from "../App"; |
| 4 | +import { ThemeContext } from "../store/Theme/ThemeContext"; |
4 | 5 |
|
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", |
9 | 11 | }; |
10 | | -}); |
11 | 12 |
|
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 }; |
16 | 23 | }); |
17 | 24 |
|
| 25 | +const mockCsCalendar = jest.fn(); |
| 26 | +let mockAnnouncementProps = null; |
| 27 | + |
18 | 28 | 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" />; |
21 | 43 | }; |
22 | 44 | }); |
23 | 45 |
|
| 46 | +jest.mock("../components/Footer", () => () => ( |
| 47 | + <div data-testid="footer">footer</div> |
| 48 | +)); |
| 49 | + |
24 | 50 | 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 | + ); |
27 | 60 | }; |
28 | 61 | }); |
29 | 62 |
|
30 | 63 | 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 | + ); |
33 | 74 | }; |
34 | 75 | }); |
35 | 76 |
|
36 | 77 | jest.mock("../components/Toastify", () => { |
37 | | - return function DummyToastify() { |
38 | | - return <div data-testid="toastify">Toastify</div>; |
39 | | - }; |
40 | | -}); |
41 | | - |
42 | | -jest.mock("../store/StoreProvider", () => { |
43 | | - return function DummyStoreProvider({ children }) { |
44 | | - return <div data-testid="store-provider">{children}</div>; |
45 | | - }; |
46 | | -}); |
47 | | - |
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), |
| 78 | + return function MockToastify({ toastifyObj }) { |
| 79 | + return ( |
| 80 | + <div data-testid="toastify" data-mode={toastifyObj?.mode || ""} /> |
| 81 | + ); |
59 | 82 | }; |
60 | 83 | }); |
61 | 84 |
|
62 | 85 | describe("App", () => { |
63 | | - it("should render without crashing", () => { |
64 | | - render(<App />); |
| 86 | + beforeEach(() => { |
| 87 | + document.body.className = ""; |
| 88 | + jest.useFakeTimers(); |
| 89 | + jest.clearAllMocks(); |
| 90 | + mockAnnouncementProps = null; |
| 91 | + mockCsCalendar.mockClear(); |
65 | 92 | }); |
66 | 93 |
|
67 | | - it("should render Header component", () => { |
68 | | - render(<App />); |
69 | | - expect(screen.getByTestId("header")).toBeInTheDocument(); |
| 94 | + afterEach(() => { |
| 95 | + jest.useRealTimers(); |
70 | 96 | }); |
71 | 97 |
|
72 | | - it("should render Footer component", () => { |
73 | | - render(<App />); |
| 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(); |
74 | 116 | expect(screen.getByTestId("footer")).toBeInTheDocument(); |
75 | 117 | }); |
76 | 118 |
|
77 | | - it("should render CSCalendar component", () => { |
78 | | - render(<App />); |
79 | | - expect(screen.getByTestId("calendar")).toBeInTheDocument(); |
| 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 | + }); |
| 127 | + |
| 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); |
80 | 133 | }); |
81 | 134 |
|
82 | | - it("should render FloatButtonSection component", () => { |
83 | | - render(<App />); |
84 | | - expect(screen.getByTestId("float-button")).toBeInTheDocument(); |
| 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(); |
85 | 143 | }); |
86 | 144 |
|
87 | | - it("should render AnnouncementModule component", () => { |
88 | | - render(<App />); |
89 | | - expect(screen.getByTestId("announcement")).toBeInTheDocument(); |
| 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 | + }); |
90 | 154 | }); |
91 | 155 |
|
92 | | - it("should render Toastify component", () => { |
93 | | - render(<App />); |
94 | | - expect(screen.getByTestId("toastify")).toBeInTheDocument(); |
| 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 | + ); |
95 | 163 | }); |
96 | 164 | }); |
0 commit comments