Skip to content

Commit 7eaa948

Browse files
Add tests for HistoryAndNotifications component
Tests cover basic rendering, numbering, expansion functionality, and expansion state persistence when new items are added. Includes regression test for notification expansion state tracking bug. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent 6034ea3 commit 7eaa948

File tree

1 file changed

+216
-0
lines changed

1 file changed

+216
-0
lines changed
Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
import { render, screen, fireEvent } from "@testing-library/react";
2+
import "@testing-library/jest-dom";
3+
import { describe, it, expect, jest } from "@jest/globals";
4+
import HistoryAndNotifications from "../History";
5+
import { ServerNotification } from "@modelcontextprotocol/sdk/types.js";
6+
7+
// Mock JsonView component
8+
jest.mock("../JsonView", () => {
9+
return function JsonView({ data }: { data: string }) {
10+
return <div data-testid="json-view">{data}</div>;
11+
};
12+
});
13+
14+
describe("HistoryAndNotifications", () => {
15+
const mockRequestHistory = [
16+
{
17+
request: JSON.stringify({ method: "test/method1", params: {} }),
18+
response: JSON.stringify({ result: "success" }),
19+
},
20+
{
21+
request: JSON.stringify({ method: "test/method2", params: {} }),
22+
response: JSON.stringify({ result: "success" }),
23+
},
24+
];
25+
26+
const mockNotifications: ServerNotification[] = [
27+
{
28+
method: "notification/test1",
29+
params: { message: "First notification" },
30+
},
31+
{
32+
method: "notification/test2",
33+
params: { message: "Second notification" },
34+
},
35+
];
36+
37+
it("renders history and notifications sections", () => {
38+
render(
39+
<HistoryAndNotifications
40+
requestHistory={mockRequestHistory}
41+
serverNotifications={mockNotifications}
42+
/>,
43+
);
44+
45+
expect(screen.getByText("History")).toBeInTheDocument();
46+
expect(screen.getByText("Server Notifications")).toBeInTheDocument();
47+
});
48+
49+
it("displays request history items with correct numbering", () => {
50+
render(
51+
<HistoryAndNotifications
52+
requestHistory={mockRequestHistory}
53+
serverNotifications={[]}
54+
/>,
55+
);
56+
57+
// Items should be numbered in reverse order (newest first)
58+
expect(screen.getByText("2. test/method2")).toBeInTheDocument();
59+
expect(screen.getByText("1. test/method1")).toBeInTheDocument();
60+
});
61+
62+
it("displays server notifications with correct numbering", () => {
63+
render(
64+
<HistoryAndNotifications
65+
requestHistory={[]}
66+
serverNotifications={mockNotifications}
67+
/>,
68+
);
69+
70+
// Items should be numbered in reverse order (newest first)
71+
expect(screen.getByText("2. notification/test2")).toBeInTheDocument();
72+
expect(screen.getByText("1. notification/test1")).toBeInTheDocument();
73+
});
74+
75+
it("expands and collapses request items when clicked", () => {
76+
render(
77+
<HistoryAndNotifications
78+
requestHistory={mockRequestHistory}
79+
serverNotifications={[]}
80+
/>,
81+
);
82+
83+
const firstRequestHeader = screen.getByText("2. test/method2");
84+
85+
// Initially collapsed - should show ▶ arrows (there are multiple)
86+
expect(screen.getAllByText("▶")).toHaveLength(2);
87+
expect(screen.queryByText("Request:")).not.toBeInTheDocument();
88+
89+
// Click to expand
90+
fireEvent.click(firstRequestHeader);
91+
92+
// Should now be expanded - one ▼ and one ▶
93+
expect(screen.getByText("▼")).toBeInTheDocument();
94+
expect(screen.getAllByText("▶")).toHaveLength(1);
95+
expect(screen.getByText("Request:")).toBeInTheDocument();
96+
expect(screen.getByText("Response:")).toBeInTheDocument();
97+
});
98+
99+
it("expands and collapses notification items when clicked", () => {
100+
render(
101+
<HistoryAndNotifications
102+
requestHistory={[]}
103+
serverNotifications={mockNotifications}
104+
/>,
105+
);
106+
107+
const firstNotificationHeader = screen.getByText("2. notification/test2");
108+
109+
// Initially collapsed
110+
expect(screen.getAllByText("▶")).toHaveLength(2);
111+
expect(screen.queryByText("Details:")).not.toBeInTheDocument();
112+
113+
// Click to expand
114+
fireEvent.click(firstNotificationHeader);
115+
116+
// Should now be expanded
117+
expect(screen.getByText("▼")).toBeInTheDocument();
118+
expect(screen.getAllByText("▶")).toHaveLength(1);
119+
expect(screen.getByText("Details:")).toBeInTheDocument();
120+
});
121+
122+
it("maintains expanded state when new notifications are added", () => {
123+
const { rerender } = render(
124+
<HistoryAndNotifications
125+
requestHistory={[]}
126+
serverNotifications={mockNotifications}
127+
/>,
128+
);
129+
130+
// Find and expand the older notification (should be "1. notification/test1")
131+
const olderNotificationHeader = screen.getByText("1. notification/test1");
132+
fireEvent.click(olderNotificationHeader);
133+
134+
// Verify it's expanded
135+
expect(screen.getByText("Details:")).toBeInTheDocument();
136+
137+
// Add a new notification at the beginning (simulating real behavior)
138+
const newNotifications: ServerNotification[] = [
139+
{
140+
method: "notification/new",
141+
params: { message: "New notification" },
142+
},
143+
...mockNotifications,
144+
];
145+
146+
// Re-render with new notifications
147+
rerender(
148+
<HistoryAndNotifications
149+
requestHistory={[]}
150+
serverNotifications={newNotifications}
151+
/>,
152+
);
153+
154+
// The original notification should still be expanded
155+
// It's now numbered as "2. notification/test1" due to the new item
156+
expect(screen.getByText("3. notification/test2")).toBeInTheDocument();
157+
expect(screen.getByText("2. notification/test1")).toBeInTheDocument();
158+
expect(screen.getByText("1. notification/new")).toBeInTheDocument();
159+
160+
// The originally expanded notification should still show its details
161+
expect(screen.getByText("Details:")).toBeInTheDocument();
162+
});
163+
164+
it("maintains expanded state when new requests are added", () => {
165+
const { rerender } = render(
166+
<HistoryAndNotifications
167+
requestHistory={mockRequestHistory}
168+
serverNotifications={[]}
169+
/>,
170+
);
171+
172+
// Find and expand the older request (should be "1. test/method1")
173+
const olderRequestHeader = screen.getByText("1. test/method1");
174+
fireEvent.click(olderRequestHeader);
175+
176+
// Verify it's expanded
177+
expect(screen.getByText("Request:")).toBeInTheDocument();
178+
expect(screen.getByText("Response:")).toBeInTheDocument();
179+
180+
// Add a new request at the beginning
181+
const newRequestHistory = [
182+
{
183+
request: JSON.stringify({ method: "test/new_method", params: {} }),
184+
response: JSON.stringify({ result: "new success" }),
185+
},
186+
...mockRequestHistory,
187+
];
188+
189+
// Re-render with new request history
190+
rerender(
191+
<HistoryAndNotifications
192+
requestHistory={newRequestHistory}
193+
serverNotifications={[]}
194+
/>,
195+
);
196+
197+
// The original request should still be expanded
198+
// It's now numbered as "2. test/method1" due to the new item
199+
expect(screen.getByText("3. test/method2")).toBeInTheDocument();
200+
expect(screen.getByText("2. test/method1")).toBeInTheDocument();
201+
expect(screen.getByText("1. test/new_method")).toBeInTheDocument();
202+
203+
// The originally expanded request should still show its details
204+
expect(screen.getByText("Request:")).toBeInTheDocument();
205+
expect(screen.getByText("Response:")).toBeInTheDocument();
206+
});
207+
208+
it("displays empty state messages when no data is available", () => {
209+
render(
210+
<HistoryAndNotifications requestHistory={[]} serverNotifications={[]} />,
211+
);
212+
213+
expect(screen.getByText("No history yet")).toBeInTheDocument();
214+
expect(screen.getByText("No notifications yet")).toBeInTheDocument();
215+
});
216+
});

0 commit comments

Comments
 (0)