119 lines
5.2 KiB
TypeScript
119 lines
5.2 KiB
TypeScript
import { renderToStaticMarkup } from "react-dom/server";
|
|
import { describe, expect, it } from "vitest";
|
|
|
|
import { PluginsPage } from "./PluginsPage";
|
|
import pluginsPageSource from "./PluginsPage.tsx?raw";
|
|
import type { MarketplacePluginResponse } from "../api/types";
|
|
|
|
const marketplacePlugin: MarketplacePluginResponse = {
|
|
id: "game.example",
|
|
name: "Example Server",
|
|
description: "Development plugin",
|
|
version: "0.1.0",
|
|
serverType: "example",
|
|
serverDisplayName: "Example Server",
|
|
supportedOs: ["linux", "darwin"],
|
|
manifestRef: "artifact://manifests/game.example/0.1.0",
|
|
createFormSchemaRef: "schemas/create-form.schema.json",
|
|
capabilities: ["process.install", "process.start", "logs.read"],
|
|
declaredPermissions: ["server.read", "server.logs.read", "ai.invoke"],
|
|
permissions: { ai: true, logs: true, files: false, jobs: true, artifacts: false },
|
|
lifecycleActions: { install: "actions/install.json", start: "actions/start.json", stop: "actions/stop.json" },
|
|
bridgeActions: ["server.instances.read", "logs.query", "ai.invoke"],
|
|
pages: [{ key: "logs", title: "Logs", path: "/logs", permissions: ["server.logs.read"], bridgeActions: ["logs.query"] }],
|
|
tags: ["example"],
|
|
aiPurposes: ["logs.diagnose"],
|
|
productionLifecycle: { operations: ["install", "enable", "disable", "upgrade", "rollback", "retire", "dependency-check"], dependencyPolicy: "optional" },
|
|
runtimeProfiles: {
|
|
dllExtensions: [{
|
|
key: "scum-simple-rcon-ue4ss",
|
|
displayName: "SCUM Simple RCON UE4SS DLL",
|
|
kind: "ue4ss-dll",
|
|
activation: "server-start",
|
|
version: "0.1.0-unpublished",
|
|
releaseState: "unpublished",
|
|
releaseHost: "cdn.npc0.com",
|
|
releaseFilename: "scum_simple_rcon_ue4s.dll",
|
|
supportedTargets: [{ os: "windows", arch: "amd64" }],
|
|
updateOnStart: true
|
|
}]
|
|
},
|
|
status: "installed",
|
|
source: "platform-registry"
|
|
};
|
|
|
|
describe("PluginsPage", () => {
|
|
it("renders the loading state before API data arrives", () => {
|
|
const html = renderToStaticMarkup(<PluginsPage initialState={{ listState: "loading" }} />);
|
|
|
|
expect(html).toContain("插件市场");
|
|
expect(html).toContain("page-summary-chip");
|
|
expect(html).not.toContain("metric-card");
|
|
expect(html).toContain("正在加载插件市场");
|
|
});
|
|
|
|
it("renders API error state without falling back in production state", () => {
|
|
const html = renderToStaticMarkup(<PluginsPage initialState={{ listState: "error", listError: "backend unavailable" }} />);
|
|
|
|
expect(html).toContain("插件市场加载失败");
|
|
expect(html).toContain("backend unavailable");
|
|
expect(html).not.toContain("本地演示数据仅用于前端开发");
|
|
});
|
|
|
|
it("renders API-backed plugin detail and state actions without unsafe fragments", () => {
|
|
const html = renderToStaticMarkup(
|
|
<PluginsPage initialState={{ listState: "ready", plugins: [marketplacePlugin], selectedId: marketplacePlugin.id, detail: marketplacePlugin }} />
|
|
);
|
|
|
|
expect(html).toContain("Example Server");
|
|
expect(html).toContain("平台 API");
|
|
expect(html).toContain("logs.query");
|
|
expect(html).toContain("logs.diagnose");
|
|
expect(html).toContain("安装");
|
|
expect(html).toContain("启用");
|
|
expect(html).toContain("停用");
|
|
expect(html).toContain("SCUM Simple RCON UE4SS DLL");
|
|
expect(html).toContain("启动前自动校验和更新");
|
|
expect(html).toContain("运行:目标服务端受监管启动");
|
|
expect(html).toContain("Linux 启动前拒绝");
|
|
expect(html).toContain('role="dialog"');
|
|
expect(html).not.toContain("billing");
|
|
expect(html).not.toContain("/Users/");
|
|
expect(html).not.toContain("unix://");
|
|
expect(html).not.toContain("Bearer ");
|
|
expect(html).not.toContain("sk-");
|
|
expect(html).not.toContain("password=");
|
|
expect(html).not.toContain("apiKeyRef");
|
|
expect(html).not.toContain("ue4ss/Mods/");
|
|
});
|
|
|
|
it("keeps standalone fallback visibly isolated", () => {
|
|
const html = renderToStaticMarkup(
|
|
<PluginsPage initialState={{ listState: "ready", plugins: [marketplacePlugin], selectedId: marketplacePlugin.id, detail: marketplacePlugin, usingFallback: true }} />
|
|
);
|
|
|
|
expect(html).toContain("本地演示数据");
|
|
expect(html).toContain("本地演示数据仅用于前端开发");
|
|
});
|
|
|
|
it("does not open a plugin detail pane until a plugin is selected", () => {
|
|
const html = renderToStaticMarkup(<PluginsPage initialState={{ listState: "ready", plugins: [marketplacePlugin] }} />);
|
|
|
|
expect(html).toContain("Example Server");
|
|
expect(html).not.toContain('role="dialog"');
|
|
expect(html).not.toContain("manifest validated");
|
|
});
|
|
|
|
it("keeps state actions confirmed, permission-gated, and detail retryable", () => {
|
|
expect(pluginsPageSource).toContain("ConfirmDialog");
|
|
expect(pluginsPageSource).toContain("retryDetail");
|
|
expect(pluginsPageSource).toContain("当前账号为只读模式");
|
|
expect(pluginsPageSource).toContain("平台会返回持久状态");
|
|
expect(pluginsPageSource).toContain("UE4SS DLL 声明");
|
|
expect(pluginsPageSource).toContain("actionPending !== null");
|
|
expect(pluginsPageSource).toContain("operations?.begin");
|
|
expect(pluginsPageSource).toContain("operations?.succeed");
|
|
expect(pluginsPageSource).toContain("operations?.fail");
|
|
});
|
|
});
|