Files
browser/platform_web/pages/ServerDetailPage.test.tsx
T

193 lines
11 KiB
TypeScript

import { describe, expect, it } from "vitest";
import { configDiffViewFromPreview } from "./ServerDetailPage";
import serversPageSource from "./ServersPage.tsx?raw";
import serverManagementTerminalSource from "../components/ServerManagementTerminalDrawer.tsx?raw";
import serverDetailPageSource from "./ServerDetailPage.tsx?raw";
import type { ServerConfigDiffPreviewResponse } from "../api/types";
const preview: ServerConfigDiffPreviewResponse = {
serverInstanceId: "server-1",
configVersion: 7,
key: "server.properties",
currentContent: "max-players=20\npvp=true\n",
proposedContent: "max-players=40\npvp=true\n",
diff: [
{ kind: "removed", oldNumber: 1, content: "max-players=20" },
{ kind: "added", newNumber: 1, content: "max-players=40" },
{ kind: "context", oldNumber: 2, newNumber: 2, content: "pvp=true" }
],
hasChanges: true,
source: "platform-review",
reviewedAt: "2026-07-06T00:00:00Z"
};
describe("ServerDetailPage config write approval", () => {
it("keeps server overview metrics on the server list instead of the detail header", () => {
expect(serverDetailPageSource).not.toContain("listServerMetrics");
expect(serverDetailPageSource).not.toContain("server-detail-stat-strip");
expect(serverDetailPageSource).not.toContain("server-detail-meter-strip");
expect(serverDetailPageSource).not.toContain("<UsageMeter");
expect(serversPageSource).toContain("listServerMetrics");
expect(serversPageSource).toContain("<UsageMeter");
});
it("keeps the detail header focused on a single shared action row", () => {
expect(serverDetailPageSource).not.toContain("Run 心跳");
expect(serverDetailPageSource).not.toContain("已自动附着");
expect(serverDetailPageSource).toContain('<span>返回列表</span>');
expect(serverDetailPageSource).toContain('<span>刷新</span>');
expect(serverDetailPageSource).toContain('<span>打开终端</span>');
expect(serverDetailPageSource).toContain('<span>启动</span>');
expect(serverDetailPageSource).toContain('<span>停止</span>');
});
it("maps platform diff preview responses into the display diff without losing approval metadata", () => {
const view = configDiffViewFromPreview(preview);
expect(view).toMatchObject({
serverInstanceId: "server-1",
configVersion: 7,
key: "server.properties",
source: "platform-review",
summary: "+1 / -1 行变更",
nextContent: "max-players=40\npvp=true\n"
});
expect(view.lines).toEqual([
{ kind: "removed", text: "max-players=20" },
{ kind: "added", text: "max-players=40" },
{ kind: "same", text: "pvp=true" }
]);
});
it("uses AI config diff approval without exposing raw config workbench APIs", () => {
expect(serverDetailPageSource).toContain("approveAIConfigDiff");
expect(serverDetailPageSource).toContain("AI 配置助手");
expect(serverDetailPageSource).not.toContain("previewServerConfigDiff");
expect(serverDetailPageSource).not.toContain("approveServerConfigWrite");
expect(serverDetailPageSource).not.toContain('capability: "config.write"');
});
it("uses typed server metadata APIs and keeps destructive deletion out of detail metadata", () => {
expect(serverDetailPageSource).toContain("updateServerInstance(instance.id");
expect(serverDetailPageSource).toContain("serverMetadataUpdateRequestFromForm");
expect(serverDetailPageSource).not.toContain("deleteServerInstance");
expect(serverDetailPageSource).not.toContain("serverDeleteConfirmation");
expect(serverDetailPageSource).not.toContain("canDeleteServer(instance.state)");
expect(serverDetailPageSource).not.toContain("请输入当前登录密码");
expect(serverDetailPageSource).not.toContain("fallbackConfig");
});
it("does not locally mutate visible config after dispatching approval jobs", () => {
expect(serverDetailPageSource).not.toContain("setCurrentConfig(suggestion.diff.nextContent)");
expect(serverDetailPageSource).not.toContain("content: diff.nextContent");
});
it("keeps artifact transfer and backend internals out of server detail", () => {
expect(serverDetailPageSource).toContain("ServerFilesSection");
expect(serverDetailPageSource).toContain("getServerFileWorkspace");
expect(serverDetailPageSource).toContain("downloadServerFileResult");
expect(serverDetailPageSource).not.toContain("openArtifactDownload");
expect(serverDetailPageSource).not.toContain("readArtifactContent");
expect(serverDetailPageSource).not.toContain("浏览器制品传输");
expect(serverDetailPageSource).not.toContain("storage://");
expect(serverDetailPageSource).not.toContain("unix://");
expect(serverDetailPageSource).not.toContain("Bearer ");
});
it("keeps the server file manager list-first without plugin declaration gates", () => {
expect(serverDetailPageSource).toContain("server-file-editor-overlay");
expect(serverDetailPageSource).toContain("browseServerFiles");
expect(serverDetailPageSource).toContain("browseList({ forceNew: true })");
expect(serverDetailPageSource).toContain("window.setTimeout(() => void browseList({ silent: true }), 500)");
expect(serverDetailPageSource).toContain("if (!options.silent) setList({ status: \"loading\" })");
expect(serverDetailPageSource).toContain("serverFileListPendingLabel");
expect(serverDetailPageSource).not.toContain("refreshRuntimeList");
expect(serverDetailPageSource).not.toContain("window.setInterval(() => void loadList");
expect(serverDetailPageSource).not.toContain("server-file-layout");
expect(serverDetailPageSource).not.toContain("未声明目录");
expect(serverDetailPageSource).not.toContain("插件尚未声明");
expect(serverDetailPageSource).not.toContain("插件声明为可编辑");
expect(serverDetailPageSource).not.toContain("entry.editable");
expect(serverDetailPageSource).not.toContain("entry.downloadable");
expect(serverDetailPageSource).not.toContain("当前目录暂无缓存结果;点击刷新目录读取实时文件。");
expect(serverDetailPageSource).not.toContain("独立通道");
expect(serverDetailPageSource).not.toContain("目录已更新");
});
it("keeps run distribution and client-manager workflows out of server detail tabs", () => {
expect(serverDetailPageSource).not.toContain('id="run-builder"');
expect(serverDetailPageSource).not.toContain("getServerRuntimeActions");
expect(serverDetailPageSource).not.toContain("generateRunDistribution");
expect(serverDetailPageSource).not.toContain("downloadLatestRunDistribution");
expect(serverDetailPageSource).not.toContain("pushRunUpdate");
expect(serverDetailPageSource).not.toContain("generateClientManager");
expect(serverDetailPageSource).not.toContain("ClientManagerLifecyclePanel");
expect(serverManagementTerminalSource).toContain("openServerLogEvents");
expect(serverDetailPageSource).not.toContain("authKey");
expect(serverDetailPageSource).not.toContain("password=");
expect(serverDetailPageSource).not.toContain("unix://");
expect(serverDetailPageSource).not.toContain("tcp://");
expect(serverDetailPageSource).not.toContain("mysql://");
expect(serverDetailPageSource).not.toContain("sqlite://");
});
it("does not expose manual runtime configuration surfaces", () => {
expect(serverDetailPageSource).not.toContain("getServerRuntimeBinding");
expect(serverDetailPageSource).not.toContain("updateServerRuntimeBinding");
expect(serverDetailPageSource).not.toContain("运行配置绑定");
expect(serverDetailPageSource).not.toContain("RuntimeDLLExtensionsPanel");
});
it("uses the historical terminal drawer without restoring separate raw command panels", () => {
expect(serverDetailPageSource).not.toContain("SourceRCONCommandPanel");
expect(serverManagementTerminalSource).toContain("dispatchSourceRCONCommand");
expect(serverDetailPageSource).not.toContain("scumManagementRCONCommandRequest");
expect(serverDetailPageSource).toContain("<span>打开终端</span>");
expect(serverDetailPageSource).toContain("ServerManagementTerminalDrawer");
expect(serverManagementTerminalSource).toContain("terminal-output-topbar");
expect(serverManagementTerminalSource).toContain("terminalQuickCommandCatalog");
expect(serverDetailPageSource).toContain("PluginPageHostPage");
});
it("removes generic plugin controls from server detail", () => {
expect(serverDetailPageSource).not.toContain("PluginControlsSection");
expect(serverDetailPageSource).not.toContain("PluginBridgeExecutionPanel");
expect(serverDetailPageSource).not.toContain("platformApiClient.createJob({");
expect(serverDetailPageSource).not.toContain("插件控制");
expect(serverDetailPageSource).toContain("platformApiClient.startServerInstance(current.id");
expect(serverDetailPageSource).toContain("platformApiClient.stopServerInstance(current.id");
expect(serverDetailPageSource).toContain("serverLifecycleCommandRequest(current, \"start\")");
expect(serverDetailPageSource).toContain("serverLifecycleCommandRequest(current, \"stop\")");
expect(serverDetailPageSource).not.toContain('capability: "process.start"');
expect(serverDetailPageSource).not.toContain('capability: "process.stop"');
});
it("leaves guided deployment to the generated Run heartbeat workflow", () => {
expect(serverDetailPageSource).not.toContain("canDeployServer(instance.data.state)");
expect(serverDetailPageSource).not.toContain("requestDeployment(instance.data)");
expect(serverDetailPageSource).not.toContain("platformApiClient.deployServerInstance(current.id");
expect(serverDetailPageSource).not.toContain('capability: "process.install"');
});
it("routes plugin-declared pages into server detail tabs", () => {
expect(serverDetailPageSource).toContain("serverDetailSectionEntries(readyPlugin)");
expect(serverDetailPageSource).toContain("plugin:${page.key}");
expect(serverDetailPageSource).toContain("ServerDetailSectionNav");
expect(serverDetailPageSource).toContain('aria-label="游戏运营功能"');
expect(serverDetailPageSource).toContain('section === "files"');
expect(serverDetailPageSource).toContain("PluginPageSection");
expect(serverDetailPageSource).toContain("PluginPageHostPage");
expect(serverDetailPageSource).not.toContain("ScumFileManagementSection");
});
it("keeps plugin lifecycle output out of raw bridge-visible detail surfaces", () => {
expect(serverDetailPageSource).not.toContain("platformApiClient.openArtifactDownload(artifact.id)");
expect(serverDetailPageSource).not.toContain("downloadArtifactReference(reference");
expect(serverDetailPageSource).not.toContain("storage://bucket");
expect(serverDetailPageSource).not.toContain("runSocket");
expect(serverDetailPageSource).not.toContain("rawApiKey");
expect(serverDetailPageSource).not.toContain("apiKeyRef");
});
});