From 03977c4b4d86ecb3f760126c9b57edce0a56675c Mon Sep 17 00:00:00 2001
From: hamster1963 <1410514192@qq.com>
Date: Sat, 20 Jun 2026 00:06:50 +0800
Subject: [PATCH] refactor: update websocket context to use structured data and
lazy load components
- Changed websocket context to use `lastData` instead of `lastMessage` for better type safety and clarity.
- Updated components to handle the new websocket data structure, including `DashCommand`, `Header`, `ServerDetail`, and various chart components.
- Introduced lazy loading for `NotFound`, `ServerDetail`, and `NetworkChart` components to improve performance.
- Added skeleton loading states for `ServerDetail` to enhance user experience during data fetching.
- Updated tests to reflect changes in websocket data handling and component structure.
---
src/App.tsx | 48 +++++++++++++++++--
src/components/DashCommand.tsx | 7 +--
src/components/Header.tsx | 8 +---
src/components/ServerDetailChart.tsx | 43 +++++++----------
src/components/ServerDetailSummary.tsx | 9 ++--
src/context/websocket-context.ts | 7 +--
src/context/websocket-provider.tsx | 32 ++++++++-----
src/hooks/use-chart-history.ts | 5 +-
src/main.tsx | 15 +++++-
src/pages/Server.tsx | 10 ++--
src/pages/ServerDetail.tsx | 25 +++++-----
src/test/components/dash-command.test.tsx | 31 ++++++------
src/test/components/header.test.tsx | 12 +++--
.../components/server-detail-chart.test.tsx | 18 ++++---
.../server-detail-components.test.tsx | 23 +++++----
src/test/context/providers.test.tsx | 39 +++++++++++++--
src/test/hooks/hooks.test.tsx | 6 +--
src/test/pages/Server.test.tsx | 26 +++++-----
src/test/pages/pages.test.tsx | 7 ++-
src/types/nezha-api.ts | 1 +
20 files changed, 225 insertions(+), 147 deletions(-)
diff --git a/src/App.tsx b/src/App.tsx
index 02b2a74..4205103 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -1,6 +1,6 @@
import { useQuery } from "@tanstack/react-query";
import type React from "react";
-import { useEffect, useState } from "react";
+import { lazy, Suspense, useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { Route, BrowserRouter as Router, Routes } from "react-router-dom";
@@ -8,15 +8,39 @@ import { DashCommand } from "./components/DashCommand";
import ErrorBoundary from "./components/ErrorBoundary";
import Footer from "./components/Footer";
import Header, { RefreshToast } from "./components/Header";
+import { Skeleton } from "./components/ui/skeleton";
import { useBackground } from "./hooks/use-background";
import { useTheme } from "./hooks/use-theme";
import { InjectContext } from "./lib/inject";
import { fetchSetting } from "./lib/nezha-api";
import { cn } from "./lib/utils";
import ErrorPage from "./pages/ErrorPage";
-import NotFound from "./pages/NotFound";
import Server from "./pages/Server";
-import ServerDetail from "./pages/ServerDetail";
+
+const NotFound = lazy(() => import("./pages/NotFound"));
+const ServerDetail = lazy(() => import("./pages/ServerDetail"));
+
+function ServerDetailRouteFallback() {
+ return (
+
+ );
+}
// Route checker component
const RouteChecker: React.FC = () => {
@@ -110,9 +134,23 @@ const MainApp: React.FC = () => {
} />
- } />
+ }>
+
+
+ }
+ />
} />
- } />
+
+
+
+ }
+ />
diff --git a/src/components/DashCommand.tsx b/src/components/DashCommand.tsx
index e913b0f..9981ff2 100644
--- a/src/components/DashCommand.tsx
+++ b/src/components/DashCommand.tsx
@@ -17,7 +17,6 @@ import { useCommand } from "@/hooks/use-command";
import { useTheme } from "@/hooks/use-theme";
import { useWebSocketContext } from "@/hooks/use-websocket-context";
import { formatNezhaInfo } from "@/lib/utils";
-import type { NezhaWebsocketResponse } from "@/types/nezha-api";
export function DashCommand() {
const { isOpen, closeCommand, toggleCommand } = useCommand();
@@ -26,11 +25,9 @@ export function DashCommand() {
const { t } = useTranslation();
const { setTheme } = useTheme();
- const { lastMessage, connected } = useWebSocketContext();
+ const { lastData, connected } = useWebSocketContext();
- const nezhaWsData = lastMessage
- ? (JSON.parse(lastMessage.data) as NezhaWebsocketResponse)
- : null;
+ const nezhaWsData = lastData;
useEffect(() => {
const down = (e: KeyboardEvent) => {
diff --git a/src/components/Header.tsx b/src/components/Header.tsx
index b2e7794..d67efbf 100644
--- a/src/components/Header.tsx
+++ b/src/components/Header.tsx
@@ -60,13 +60,9 @@ function Header() {
refetchOnWindowFocus: true,
});
- const { lastMessage, connected } = useWebSocketContext();
+ const { lastData, connected } = useWebSocketContext();
- const onlineCount = connected
- ? lastMessage
- ? JSON.parse(lastMessage.data).online || 0
- : 0
- : "...";
+ const onlineCount = connected ? (lastData ? lastData.online || 0 : 0) : "...";
const siteName = settingData?.data?.config?.site_name;
diff --git a/src/components/ServerDetailChart.tsx b/src/components/ServerDetailChart.tsx
index 21176ff..2a829e4 100644
--- a/src/components/ServerDetailChart.tsx
+++ b/src/components/ServerDetailChart.tsx
@@ -211,7 +211,7 @@ export default function ServerDetailChart({
}: {
server_id: string;
}) {
- const { lastMessage, connected, messageHistory } = useWebSocketContext();
+ const { lastData, connected, messageHistory } = useWebSocketContext();
const [selectedPeriod, setSelectedPeriod] = useState("realtime");
// Check if user is logged in
@@ -256,13 +256,11 @@ export default function ServerDetailChart({
}
}, [isLogin, isTsdbEnabled, selectedPeriod]);
- if (!connected && !lastMessage) {
+ if (!connected && !lastData) {
return ;
}
- const nezhaWsData = lastMessage
- ? (JSON.parse(lastMessage.data) as NezhaWebsocketResponse)
- : null;
+ const nezhaWsData = lastData;
if (!nezhaWsData) {
return ;
@@ -436,7 +434,7 @@ function GpuChart({
index: number;
gpuStat: number;
gpuName?: string;
- messageHistory: { data: string }[];
+ messageHistory: NezhaWebsocketResponse[];
period: ChartPeriod;
}) {
const [gpuChartData, setGpuChartData] = useState([]);
@@ -467,8 +465,7 @@ function GpuChart({
messageHistory.length > 0
) {
const historyData = messageHistory
- .map((msg) => {
- const wsData = JSON.parse(msg.data) as NezhaWebsocketResponse;
+ .map((wsData) => {
const server = wsData.servers.find((s) => s.id === id);
if (!server) return null;
const { gpu } = formatNezhaInfo(wsData.now, server);
@@ -630,7 +627,7 @@ function CpuChart({
}: {
now: number;
data: NezhaServer;
- messageHistory: { data: string }[];
+ messageHistory: NezhaWebsocketResponse[];
period: ChartPeriod;
}) {
const [cpuChartData, setCpuChartData] = useState([]);
@@ -663,8 +660,7 @@ function CpuChart({
messageHistory.length > 0
) {
const historyData = messageHistory
- .map((msg) => {
- const wsData = JSON.parse(msg.data) as NezhaWebsocketResponse;
+ .map((wsData) => {
const server = wsData.servers.find((s) => s.id === data.id);
if (!server) return null;
const { cpu } = formatNezhaInfo(wsData.now, server);
@@ -824,7 +820,7 @@ function ProcessChart({
}: {
now: number;
data: NezhaServer;
- messageHistory: { data: string }[];
+ messageHistory: NezhaWebsocketResponse[];
period: ChartPeriod;
}) {
const { t } = useTranslation();
@@ -865,8 +861,7 @@ function ProcessChart({
messageHistory.length > 0
) {
const historyData = messageHistory
- .map((msg) => {
- const wsData = JSON.parse(msg.data) as NezhaWebsocketResponse;
+ .map((wsData) => {
const server = wsData.servers.find((s) => s.id === data.id);
if (!server) return null;
const { process } = formatNezhaInfo(wsData.now, server);
@@ -1020,7 +1015,7 @@ function MemChart({
}: {
now: number;
data: NezhaServer;
- messageHistory: { data: string }[];
+ messageHistory: NezhaWebsocketResponse[];
period: ChartPeriod;
}) {
const { t } = useTranslation();
@@ -1121,8 +1116,7 @@ function MemChart({
messageHistory.length > 0
) {
const historyData = messageHistory
- .map((msg) => {
- const wsData = JSON.parse(msg.data) as NezhaWebsocketResponse;
+ .map((wsData) => {
const server = wsData.servers.find((s) => s.id === data.id);
if (!server) return null;
const { mem, swap } = formatNezhaInfo(wsData.now, server);
@@ -1339,7 +1333,7 @@ function DiskChart({
}: {
now: number;
data: NezhaServer;
- messageHistory: { data: string }[];
+ messageHistory: NezhaWebsocketResponse[];
period: ChartPeriod;
}) {
const { t } = useTranslation();
@@ -1383,8 +1377,7 @@ function DiskChart({
messageHistory.length > 0
) {
const historyData = messageHistory
- .map((msg) => {
- const wsData = JSON.parse(msg.data) as NezhaWebsocketResponse;
+ .map((wsData) => {
const server = wsData.servers.find((s) => s.id === data.id);
if (!server) return null;
const { disk } = formatNezhaInfo(wsData.now, server);
@@ -1553,7 +1546,7 @@ function NetworkChart({
}: {
now: number;
data: NezhaServer;
- messageHistory: { data: string }[];
+ messageHistory: NezhaWebsocketResponse[];
period: ChartPeriod;
}) {
const { t } = useTranslation();
@@ -1645,8 +1638,7 @@ function NetworkChart({
messageHistory.length > 0
) {
const historyData = messageHistory
- .map((msg) => {
- const wsData = JSON.parse(msg.data) as NezhaWebsocketResponse;
+ .map((wsData) => {
const server = wsData.servers.find((s) => s.id === data.id);
if (!server) return null;
const { up, down } = formatNezhaInfo(wsData.now, server);
@@ -1861,7 +1853,7 @@ function ConnectChart({
}: {
now: number;
data: NezhaServer;
- messageHistory: { data: string }[];
+ messageHistory: NezhaWebsocketResponse[];
period: ChartPeriod;
}) {
const [connectChartData, setConnectChartData] = useState(
@@ -1951,8 +1943,7 @@ function ConnectChart({
messageHistory.length > 0
) {
const historyData = messageHistory
- .map((msg) => {
- const wsData = JSON.parse(msg.data) as NezhaWebsocketResponse;
+ .map((wsData) => {
const server = wsData.servers.find((s) => s.id === data.id);
if (!server) return null;
const { tcp, udp } = formatNezhaInfo(wsData.now, server);
diff --git a/src/components/ServerDetailSummary.tsx b/src/components/ServerDetailSummary.tsx
index f6f3bc1..16546b1 100644
--- a/src/components/ServerDetailSummary.tsx
+++ b/src/components/ServerDetailSummary.tsx
@@ -3,22 +3,19 @@
import { Progress } from "@/components/ui/progress";
import { useWebSocketContext } from "@/hooks/use-websocket-context";
import { formatNezhaInfo } from "@/lib/utils";
-import type { NezhaWebsocketResponse } from "@/types/nezha-api";
export default function ServerDetailSummary({
server_id,
}: {
server_id: number;
}) {
- const { lastMessage, connected } = useWebSocketContext();
+ const { lastData, connected } = useWebSocketContext();
- if (!connected && !lastMessage) {
+ if (!connected && !lastData) {
return null;
}
- const nezhaWsData = lastMessage
- ? (JSON.parse(lastMessage.data) as NezhaWebsocketResponse)
- : null;
+ const nezhaWsData = lastData;
if (!nezhaWsData) {
return null;
diff --git a/src/context/websocket-context.ts b/src/context/websocket-context.ts
index e309c60..f0add70 100644
--- a/src/context/websocket-context.ts
+++ b/src/context/websocket-context.ts
@@ -1,16 +1,17 @@
import { createContext } from "react";
+import type { NezhaWebsocketResponse } from "@/types/nezha-api";
export interface WebSocketContextType {
- lastMessage: { data: string } | null;
+ lastData: NezhaWebsocketResponse | null;
connected: boolean;
- messageHistory: { data: string }[];
+ messageHistory: NezhaWebsocketResponse[];
reconnect: () => void;
needReconnect: boolean;
setNeedReconnect: (needReconnect: boolean) => void;
}
export const WebSocketContext = createContext({
- lastMessage: null,
+ lastData: null,
connected: false,
messageHistory: [],
reconnect: () => {},
diff --git a/src/context/websocket-provider.tsx b/src/context/websocket-provider.tsx
index eaebdca..55c77c1 100644
--- a/src/context/websocket-provider.tsx
+++ b/src/context/websocket-provider.tsx
@@ -1,6 +1,6 @@
import type React from "react";
import { useCallback, useEffect, useRef, useState } from "react";
-
+import type { NezhaWebsocketResponse } from "@/types/nezha-api";
import {
WebSocketContext,
type WebSocketContextType,
@@ -15,8 +15,10 @@ export const WebSocketProvider: React.FC = ({
url,
children,
}) => {
- const [lastMessage, setLastMessage] = useState<{ data: string } | null>(null);
- const [messageHistory, setMessageHistory] = useState<{ data: string }[]>([]); // 新增历史消息状态
+ const [lastData, setLastData] = useState(null);
+ const [messageHistory, setMessageHistory] = useState<
+ NezhaWebsocketResponse[]
+ >([]);
const [connected, setConnected] = useState(false);
const [needReconnect, setNeedReconnect] = useState(false);
const ws = useRef(null);
@@ -85,13 +87,21 @@ export const WebSocketProvider: React.FC = ({
};
ws.current.onmessage = (event) => {
- const newMessage = { data: event.data };
- setLastMessage(newMessage);
- // 更新历史消息,保持最新的30条记录
- setMessageHistory((prev) => {
- const updated = [newMessage, ...prev];
- return updated.slice(0, 30);
- });
+ try {
+ if (typeof event.data !== "string") {
+ throw new Error("WebSocket message data must be a string");
+ }
+
+ const newData = JSON.parse(event.data) as NezhaWebsocketResponse;
+ setLastData(newData);
+ // 更新历史消息,保持最新的30条记录
+ setMessageHistory((prev) => {
+ const updated = [newData, ...prev];
+ return updated.slice(0, 30);
+ });
+ } catch (error) {
+ console.error("Failed to parse WebSocket message:", error);
+ }
};
ws.current.onerror = (error) => {
@@ -130,7 +140,7 @@ export const WebSocketProvider: React.FC = ({
}, [cleanup, connect]);
const contextValue: WebSocketContextType = {
- lastMessage,
+ lastData,
connected,
messageHistory,
reconnect,
diff --git a/src/hooks/use-chart-history.ts b/src/hooks/use-chart-history.ts
index f5f1a38..09cdf0f 100644
--- a/src/hooks/use-chart-history.ts
+++ b/src/hooks/use-chart-history.ts
@@ -2,7 +2,7 @@ import { useEffect, useState } from "react";
import type { NezhaWebsocketResponse } from "@/types/nezha-api";
export function useChartHistory(
- messageHistory: { data: string }[],
+ messageHistory: NezhaWebsocketResponse[],
serverId: number,
formatFn: (wsData: NezhaWebsocketResponse, serverId: number) => T | null,
) {
@@ -11,8 +11,7 @@ export function useChartHistory(
useEffect(() => {
if (messageHistory.length > 0 && data.length === 0) {
const historyData = messageHistory
- .map((msg) => {
- const wsData = JSON.parse(msg.data) as NezhaWebsocketResponse;
+ .map((wsData) => {
return formatFn(wsData, serverId);
})
.filter((item): item is T => item !== null)
diff --git a/src/main.tsx b/src/main.tsx
index f328a2e..f7bf293 100644
--- a/src/main.tsx
+++ b/src/main.tsx
@@ -1,5 +1,5 @@
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
-import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
+import { lazy, Suspense } from "react";
import ReactDOM from "react-dom/client";
import { Toaster } from "sonner";
@@ -15,6 +15,13 @@ import "./i18n";
import "./index.css";
const queryClient = new QueryClient();
+const ReactQueryDevtools = import.meta.env.DEV
+ ? lazy(() =>
+ import("@tanstack/react-query-devtools").then((module) => ({
+ default: module.ReactQueryDevtools,
+ })),
+ )
+ : null;
const rootElement = document.getElementById("root");
if (!rootElement) {
@@ -42,7 +49,11 @@ ReactDOM.createRoot(rootElement).render(
position="top-center"
className={"flex items-center justify-center"}
/>
-
+ {ReactQueryDevtools ? (
+
+
+
+ ) : null}
diff --git a/src/pages/Server.tsx b/src/pages/Server.tsx
index cd56e04..104d9c0 100644
--- a/src/pages/Server.tsx
+++ b/src/pages/Server.tsx
@@ -22,7 +22,7 @@ import { useStatus } from "@/hooks/use-status";
import { useWebSocketContext } from "@/hooks/use-websocket-context";
import { fetchServerGroup, fetchService } from "@/lib/nezha-api";
import { cn, formatNezhaInfo } from "@/lib/utils";
-import type { NezhaWebsocketResponse, ServerGroup } from "@/types/nezha-api";
+import type { ServerGroup } from "@/types/nezha-api";
export default function Servers() {
const { t } = useTranslation();
@@ -41,7 +41,7 @@ export default function Servers() {
const hasServices =
!!serviceData?.data?.services &&
Object.keys(serviceData.data.services).length > 0;
- const { lastMessage, connected } = useWebSocketContext();
+ const { lastData, connected } = useWebSocketContext();
const { status } = useStatus();
const [showServices, setShowServices] = useState("0");
const [showMap, setShowMap] = useState("0");
@@ -124,9 +124,7 @@ export default function Servers() {
restoreScrollPosition();
}, [restoreScrollPosition]);
- const nezhaWsData = lastMessage
- ? (JSON.parse(lastMessage.data) as NezhaWebsocketResponse)
- : null;
+ const nezhaWsData = lastData;
const groupTabs = [
"All",
@@ -142,7 +140,7 @@ export default function Servers() {
?.map((item: ServerGroup) => item.group.name) || []),
];
- if (!connected && !lastMessage) {
+ if (!connected && !lastData) {
return (
diff --git a/src/pages/ServerDetail.tsx b/src/pages/ServerDetail.tsx
index eb1c238..dac573d 100644
--- a/src/pages/ServerDetail.tsx
+++ b/src/pages/ServerDetail.tsx
@@ -1,11 +1,17 @@
-import { useEffect, useState } from "react";
+import { lazy, Suspense, useEffect, useState } from "react";
import { Navigate, useParams } from "react-router-dom";
-import { NetworkChart } from "@/components/NetworkChart";
+import NetworkChartLoading from "@/components/NetworkChartLoading";
import ServerDetailChart from "@/components/ServerDetailChart";
import ServerDetailOverview from "@/components/ServerDetailOverview";
import TabSwitch from "@/components/TabSwitch";
import { Separator } from "@/components/ui/separator";
+const NetworkChart = lazy(() =>
+ import("@/components/NetworkChart").then((module) => ({
+ default: module.NetworkChart,
+ })),
+);
+
export default function ServerDetail() {
useEffect(() => {
window.scrollTo({ top: 0, left: 0, behavior: "instant" });
@@ -39,15 +45,12 @@ export default function ServerDetail() {
*/}
-
-
-
-
-
-
+ {currentTab === tabs[0] &&
}
+ {currentTab === tabs[1] && (
+
}>
+
+
+ )}
);
}
diff --git a/src/test/components/dash-command.test.tsx b/src/test/components/dash-command.test.tsx
index 14d89e7..20ef0eb 100644
--- a/src/test/components/dash-command.test.tsx
+++ b/src/test/components/dash-command.test.tsx
@@ -7,7 +7,10 @@ import { createServer } from "@/test/fixtures";
const dashMocks = vi.hoisted(() => ({
closeCommand: vi.fn(),
isOpen: true,
- lastMessage: null as MessageEvent
| null,
+ lastData: null as {
+ now: number;
+ servers: ReturnType[];
+ } | null,
navigate: vi.fn(),
setTheme: vi.fn(),
toggleCommand: vi.fn(),
@@ -31,7 +34,7 @@ vi.mock("@/hooks/use-theme", () => ({
vi.mock("@/hooks/use-websocket-context", () => ({
useWebSocketContext: () => ({
connected: dashMocks.connected,
- lastMessage: dashMocks.lastMessage,
+ lastData: dashMocks.lastData,
}),
}));
@@ -45,24 +48,22 @@ vi.mock("react-router-dom", async (importOriginal) => {
function seedWebSocketData() {
dashMocks.connected = true;
- dashMocks.lastMessage = new MessageEvent("message", {
- data: JSON.stringify({
- now: Date.parse("2025-01-01T00:00:00.000Z") / 1000,
- servers: [
- createServer({
- id: 7,
- name: "edge-7",
- last_active: "2025-01-01T00:00:00.000Z",
- }),
- ],
- }),
- });
+ dashMocks.lastData = {
+ now: Date.parse("2025-01-01T00:00:20.000Z"),
+ servers: [
+ createServer({
+ id: 7,
+ name: "edge-7",
+ last_active: "2025-01-01T00:00:00.000Z",
+ }),
+ ],
+ };
}
describe("DashCommand", () => {
it("does not render until websocket data is available", () => {
dashMocks.connected = false;
- dashMocks.lastMessage = null;
+ dashMocks.lastData = null;
const { container } = render();
diff --git a/src/test/components/header.test.tsx b/src/test/components/header.test.tsx
index 435c45e..b97c071 100644
--- a/src/test/components/header.test.tsx
+++ b/src/test/components/header.test.tsx
@@ -15,7 +15,7 @@ const headerMocks = vi.hoisted(() => ({
connected: true,
fetchLoginUser: vi.fn(),
fetchSetting: vi.fn(),
- lastMessage: null as MessageEvent | null,
+ lastData: null as { now: number; online?: number; servers: [] } | null,
needReconnect: false,
setNeedReconnect: vi.fn(),
updateBackground: vi.fn(),
@@ -37,7 +37,7 @@ vi.mock("@/hooks/use-background", () => ({
vi.mock("@/hooks/use-websocket-context", () => ({
useWebSocketContext: () => ({
connected: headerMocks.connected,
- lastMessage: headerMocks.lastMessage,
+ lastData: headerMocks.lastData,
needReconnect: headerMocks.needReconnect,
setNeedReconnect: headerMocks.setNeedReconnect,
}),
@@ -104,9 +104,11 @@ describe("Header", () => {
headerMocks.connected = true;
headerMocks.fetchLoginUser.mockReset();
headerMocks.fetchSetting.mockReset();
- headerMocks.lastMessage = new MessageEvent("message", {
- data: JSON.stringify({ online: 4 }),
- });
+ headerMocks.lastData = {
+ now: Date.parse("2025-01-01T00:00:20.000Z"),
+ online: 4,
+ servers: [],
+ };
headerMocks.needReconnect = false;
headerMocks.setNeedReconnect.mockReset();
headerMocks.updateBackground.mockReset();
diff --git a/src/test/components/server-detail-chart.test.tsx b/src/test/components/server-detail-chart.test.tsx
index 29b3bb0..0e5bd47 100644
--- a/src/test/components/server-detail-chart.test.tsx
+++ b/src/test/components/server-detail-chart.test.tsx
@@ -6,15 +6,15 @@ import { beforeEach, describe, expect, it, vi } from "vitest";
import ServerDetailChart from "@/components/ServerDetailChart";
import { createServer, createSettingResponse } from "@/test/fixtures";
import { createTestQueryClient } from "@/test/utils";
-import type { NezhaServer } from "@/types/nezha-api";
+import type { NezhaServer, NezhaWebsocketResponse } from "@/types/nezha-api";
const detailChartMocks = vi.hoisted(() => ({
connected: true,
fetchLoginUser: vi.fn(),
fetchServerMetrics: vi.fn(),
fetchSetting: vi.fn(),
- lastMessage: null as { data: string } | null,
- messageHistory: [] as { data: string }[],
+ lastData: null as NezhaWebsocketResponse | null,
+ messageHistory: [] as NezhaWebsocketResponse[],
}));
vi.mock("recharts", () => {
@@ -70,7 +70,7 @@ vi.mock("recharts", () => {
vi.mock("@/hooks/use-websocket-context", () => ({
useWebSocketContext: () => ({
connected: detailChartMocks.connected,
- lastMessage: detailChartMocks.lastMessage,
+ lastData: detailChartMocks.lastData,
messageHistory: detailChartMocks.messageHistory,
}),
}));
@@ -122,10 +122,8 @@ function metricsResponse(metric: string) {
function websocketPayload(server: NezhaServer, now: number) {
return {
- data: JSON.stringify({
- now,
- servers: [server],
- }),
+ now,
+ servers: [server],
};
}
@@ -160,7 +158,7 @@ function seedWebSocketData() {
});
detailChartMocks.connected = true;
- detailChartMocks.lastMessage = websocketPayload(server, baseNow);
+ detailChartMocks.lastData = websocketPayload(server, baseNow);
detailChartMocks.messageHistory = [0, 1, 2].map((index) =>
websocketPayload(
createServer({
@@ -192,7 +190,7 @@ describe("ServerDetailChart", () => {
detailChartMocks.fetchLoginUser.mockReset();
detailChartMocks.fetchServerMetrics.mockReset();
detailChartMocks.fetchSetting.mockReset();
- detailChartMocks.lastMessage = null;
+ detailChartMocks.lastData = null;
detailChartMocks.messageHistory = [];
detailChartMocks.fetchLoginUser.mockRejectedValue(new Error("anonymous"));
detailChartMocks.fetchSetting.mockResolvedValue(settingResponse());
diff --git a/src/test/components/server-detail-components.test.tsx b/src/test/components/server-detail-components.test.tsx
index 8b6d526..5983446 100644
--- a/src/test/components/server-detail-components.test.tsx
+++ b/src/test/components/server-detail-components.test.tsx
@@ -8,7 +8,14 @@ import { createServer } from "@/test/fixtures";
const websocketMocks = vi.hoisted(() => ({
connected: true,
- lastMessage: null as MessageEvent | null,
+ lastData: null as {
+ now: number;
+ servers: ReturnType[];
+ } | null,
+}));
+
+vi.mock("@numeric-text/react", () => ({
+ default: ({ value }: { value: string | number }) => {value},
}));
vi.mock("@/hooks/use-websocket-context", () => ({
@@ -20,12 +27,10 @@ function seedWebSocketData({
now = Date.parse("2025-01-01T00:00:20.000Z"),
} = {}) {
websocketMocks.connected = true;
- websocketMocks.lastMessage = new MessageEvent("message", {
- data: JSON.stringify({
- now,
- servers: [server],
- }),
- });
+ websocketMocks.lastData = {
+ now,
+ servers: [server],
+ };
}
function LocationProbe() {
@@ -36,7 +41,7 @@ function LocationProbe() {
describe("ServerDetailSummary", () => {
beforeEach(() => {
websocketMocks.connected = true;
- websocketMocks.lastMessage = null;
+ websocketMocks.lastData = null;
});
it("renders nothing until websocket data exists", () => {
@@ -68,7 +73,7 @@ describe("ServerDetailSummary", () => {
describe("ServerDetailOverview", () => {
beforeEach(() => {
websocketMocks.connected = true;
- websocketMocks.lastMessage = null;
+ websocketMocks.lastData = null;
Object.assign(window, { ForceUseSvgFlag: true });
});
diff --git a/src/test/context/providers.test.tsx b/src/test/context/providers.test.tsx
index 5e54cfd..cf4feb3 100644
--- a/src/test/context/providers.test.tsx
+++ b/src/test/context/providers.test.tsx
@@ -12,6 +12,7 @@ import { useSort } from "@/hooks/use-sort";
import { useStatus } from "@/hooks/use-status";
import { useTooltip } from "@/hooks/use-tooltip";
import { useWebSocketContext } from "@/hooks/use-websocket-context";
+import { createServer } from "@/test/fixtures";
class FakeWebSocket {
static readonly CONNECTING = 0;
@@ -121,7 +122,7 @@ function TooltipProbe() {
function WebSocketProbe() {
const {
connected,
- lastMessage,
+ lastData,
messageHistory,
needReconnect,
setNeedReconnect,
@@ -130,7 +131,7 @@ function WebSocketProbe() {
return (
{connected ? "connected" : "disconnected"}
-
{lastMessage?.data ?? "none"}
+
{lastData?.servers[0]?.name ?? "none"}
{messageHistory.length}
{needReconnect ? "needs-reconnect" : "stable"}