mirror of
https://github.com/Buriburizaem0n/nezha-dash-v1.git
synced 2026-09-20 18:20:13 +00:00
feat: enhance server handling and add empty state messaging in Server component
This commit is contained in:
@@ -132,7 +132,10 @@ function WebSocketProbe() {
|
||||
<div>
|
||||
<p>{connected ? "connected" : "disconnected"}</p>
|
||||
<p>{lastData?.servers[0]?.name ?? "none"}</p>
|
||||
<p>{messageHistory.length}</p>
|
||||
<p data-testid="message-count">{messageHistory.length}</p>
|
||||
<p data-testid="history-server-count">
|
||||
{messageHistory.reduce((total, item) => total + item.servers.length, 0)}
|
||||
</p>
|
||||
<p>{needReconnect ? "needs-reconnect" : "stable"}</p>
|
||||
<button type="button" onClick={() => setNeedReconnect(true)}>
|
||||
mark
|
||||
@@ -268,7 +271,24 @@ describe("WebSocketProvider", () => {
|
||||
});
|
||||
|
||||
expect(screen.getByText("second")).toBeInTheDocument();
|
||||
expect(screen.getByText("2")).toBeInTheDocument();
|
||||
expect(screen.getByTestId("message-count")).toHaveTextContent("2");
|
||||
});
|
||||
|
||||
it("normalizes missing and non-array server collections", () => {
|
||||
renderWebSocketProvider(<WebSocketProbe />);
|
||||
const socket = FakeWebSocket.instances[0];
|
||||
const now = Date.parse("2025-01-01T00:00:20.000Z");
|
||||
|
||||
act(() => {
|
||||
socket.open();
|
||||
socket.message(JSON.stringify({ now }));
|
||||
socket.message(JSON.stringify({ now, servers: null }));
|
||||
socket.message(JSON.stringify({ now, servers: { invalid: true } }));
|
||||
});
|
||||
|
||||
expect(screen.getByText("none")).toBeInTheDocument();
|
||||
expect(screen.getByTestId("message-count")).toHaveTextContent("3");
|
||||
expect(screen.getByTestId("history-server-count")).toHaveTextContent("0");
|
||||
});
|
||||
|
||||
it("keeps only the latest thirty websocket messages", () => {
|
||||
@@ -283,7 +303,7 @@ describe("WebSocketProvider", () => {
|
||||
});
|
||||
|
||||
expect(screen.getByText("message-30")).toBeInTheDocument();
|
||||
expect(screen.getByText("30")).toBeInTheDocument();
|
||||
expect(screen.getByTestId("message-count")).toHaveTextContent("30");
|
||||
});
|
||||
|
||||
it("ignores malformed websocket messages without disconnecting", () => {
|
||||
@@ -300,13 +320,34 @@ describe("WebSocketProvider", () => {
|
||||
|
||||
expect(screen.getByText("connected")).toBeInTheDocument();
|
||||
expect(screen.getByText("none")).toBeInTheDocument();
|
||||
expect(screen.getByText("0")).toBeInTheDocument();
|
||||
expect(screen.getByTestId("message-count")).toHaveTextContent("0");
|
||||
expect(consoleError).toHaveBeenCalledWith(
|
||||
"Failed to parse WebSocket message:",
|
||||
expect.any(SyntaxError),
|
||||
);
|
||||
});
|
||||
|
||||
it("ignores websocket messages without a valid response shape", () => {
|
||||
const consoleError = vi
|
||||
.spyOn(console, "error")
|
||||
.mockImplementation(() => undefined);
|
||||
renderWebSocketProvider(<WebSocketProbe />);
|
||||
const socket = FakeWebSocket.instances[0];
|
||||
|
||||
act(() => {
|
||||
socket.open();
|
||||
socket.message("null");
|
||||
socket.message(JSON.stringify({ servers: [] }));
|
||||
});
|
||||
|
||||
expect(screen.getByTestId("message-count")).toHaveTextContent("0");
|
||||
expect(consoleError).toHaveBeenCalledTimes(2);
|
||||
expect(consoleError).toHaveBeenCalledWith(
|
||||
"Failed to parse WebSocket message:",
|
||||
expect.any(TypeError),
|
||||
);
|
||||
});
|
||||
|
||||
it("exposes manual reconnect state separately from socket state", async () => {
|
||||
const user = userEvent.setup();
|
||||
renderWebSocketProvider(<WebSocketProbe />);
|
||||
|
||||
@@ -241,6 +241,45 @@ describe("Servers page", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("shows an empty state and hides controls when there are no servers", () => {
|
||||
const { container } = renderServerPage({
|
||||
connected: true,
|
||||
lastData: websocketPayload([]),
|
||||
});
|
||||
|
||||
expect(screen.getByTestId("server-overview")).toHaveTextContent("0:0:0");
|
||||
expect(screen.getByText("info.noServers")).toBeInTheDocument();
|
||||
expect(
|
||||
container.querySelector(".server-overview-controls"),
|
||||
).not.toBeVisible();
|
||||
expect(screen.queryByTestId("server-card")).not.toBeInTheDocument();
|
||||
expect(screen.queryByTestId("global-map")).not.toBeInTheDocument();
|
||||
expect(screen.queryByTestId("service-tracker")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("shows a filtered empty state while keeping controls available", async () => {
|
||||
const offline = createServer({
|
||||
id: 1,
|
||||
name: "offline",
|
||||
last_active: "2024-12-31T23:00:00.000Z",
|
||||
});
|
||||
const user = userEvent.setup();
|
||||
|
||||
const { container } = renderServerPage(
|
||||
{
|
||||
connected: true,
|
||||
lastData: websocketPayload([offline]),
|
||||
},
|
||||
{ withStatusControl: true },
|
||||
);
|
||||
|
||||
await user.click(screen.getByRole("button", { name: "online-only" }));
|
||||
|
||||
expect(screen.getByText("info.noMatchingServers")).toBeInTheDocument();
|
||||
expect(container.querySelector(".server-overview-controls")).not.toBeNull();
|
||||
expect(screen.queryByTestId("server-card")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("filters servers by selected group", async () => {
|
||||
const online = createServer({ id: 1, name: "alpha" });
|
||||
const offline = createServer({
|
||||
|
||||
Reference in New Issue
Block a user