Fix SCUM file workbench interactions
This commit is contained in:
@@ -9,6 +9,19 @@ import { configurationCatalog, validateConfigPatch, validateStatePatch, validate
|
||||
import { scumMigrationParityFixtures } from "./fixtures/scum-migration-parity.js";
|
||||
|
||||
const pageSource = readFileSync(resolve(dirname(fileURLToPath(import.meta.url)), "../examples/scum-server-plugin/features/page.ts"), "utf8");
|
||||
const declaredWorkspace = {
|
||||
defaultDirectoryKey: "scum-config",
|
||||
directories: [
|
||||
{ key: "scum-config", label: "服务器配置", scope: "config" },
|
||||
{ key: "scum-logs", label: "日志文件", scope: "logs" }
|
||||
],
|
||||
files: [
|
||||
{ key: "scum-server-settings", directoryKey: "scum-config", label: "ServerSettings.ini", kind: "config", editable: true },
|
||||
{ key: "scum-game-config", directoryKey: "scum-config", label: "Game.ini", kind: "config" },
|
||||
{ key: "scum-admin-log", directoryKey: "scum-logs", label: "Admin.log", kind: "log", streamKey: "scum.admin" }
|
||||
],
|
||||
configFields: configurationCatalog
|
||||
};
|
||||
|
||||
describe("SCUM plugin feature module", () => {
|
||||
it("owns runtime allowlists without a version gate", () => {
|
||||
@@ -56,17 +69,27 @@ describe("SCUM plugin feature module", () => {
|
||||
expect(view.nodes).toContain("section:SCUM 文件管理");
|
||||
expect(view.nodes).toContain("aside:SCUM 文件两级菜单");
|
||||
expect(view.nodes).toContain("article:文件 ServerSettings.ini");
|
||||
expect(view.texts.join("\n")).toContain("/SCUM/Saved/Config/WindowsServer");
|
||||
expect(view.texts.join("\n")).toContain("/SCUM/Saved/SaveFiles/Logs");
|
||||
expect(view.texts.join("\n")).toContain("scum-config · 配置声明");
|
||||
expect(view.texts).toContain("刷新文件列表");
|
||||
expect(view.texts).toContain("ServerSettings.ini");
|
||||
expect(view.texts).toContain("Game.ini");
|
||||
expect(view.texts).toContain("配置表单");
|
||||
expect(view.texts).toContain("键值视图");
|
||||
expect(view.texts).toContain("原文模式");
|
||||
expect(view.buttons.find((button) => button.label === "刷新文件列表")?.disabled).toBe(false);
|
||||
expect(view.buttons.find((button) => button.label === "读取文件")?.disabled).toBe(false);
|
||||
expect(view.buttons.find((button) => button.label === "刷新结果")?.disabled).toBe(false);
|
||||
for (const legacyText of ["玩家档案", "礼物", "受控状态", "载具", "地图轨迹", "查询玩家"]) expect(view.texts.join("\n")).not.toContain(legacyText);
|
||||
});
|
||||
|
||||
it("does not invent a hardcoded SCUM file list when the platform workspace is missing", () => {
|
||||
const view = renderAndCollect({ workspace: {} });
|
||||
expect(view.texts.join("\n")).toContain("当前插件没有可展示的声明文件。");
|
||||
expect(view.texts).not.toContain("ServerSettings.ini");
|
||||
expect(view.texts).not.toContain("Game.ini");
|
||||
expect(pageSource).not.toContain("fallbackFiles");
|
||||
});
|
||||
|
||||
it("keeps declared log files in read-only raw view with encoding controls", () => {
|
||||
const view = renderAndCollect({ directoryKey: "scum-logs", fileKey: "scum-admin-log" });
|
||||
expect(view.nodes).toContain("article:文件 Admin.log");
|
||||
@@ -90,15 +113,15 @@ describe("SCUM plugin feature module", () => {
|
||||
expect(view.buttons.find((button) => button.label === "提交写入")?.disabled).toBe(true);
|
||||
});
|
||||
|
||||
it("loads snapshots on selection or manual refresh without interval polling", () => {
|
||||
it("loads snapshots on selection or manual refresh and bounded post-request refresh", () => {
|
||||
expect(pageSource).toContain("getFileSnapshot(selectedFile.key)");
|
||||
expect(pageSource).toContain("刷新结果");
|
||||
expect(pageSource).toContain("loadFileSnapshot(selectedFile.key, true)");
|
||||
expect(pageSource).not.toContain("setInterval");
|
||||
expect(pageSource).not.toContain("setTimeout");
|
||||
});
|
||||
});
|
||||
|
||||
function renderAndCollect(options: { snapshot?: Record<string, unknown>; permissions?: string[]; directoryKey?: string; fileKey?: string } = {}) {
|
||||
function renderAndCollect(options: { snapshot?: Record<string, unknown>; permissions?: string[]; directoryKey?: string; fileKey?: string; workspace?: Record<string, unknown> } = {}) {
|
||||
const nodes: string[] = [];
|
||||
const texts: string[] = [];
|
||||
const buttons: Array<{ label: string; disabled: boolean }> = [];
|
||||
@@ -116,15 +139,16 @@ function renderAndCollect(options: { snapshot?: Record<string, unknown>; permiss
|
||||
stateCall += 1;
|
||||
if (stateCall === 1 && options.directoryKey) return [options.directoryKey as T, () => undefined];
|
||||
if (stateCall === 2 && options.fileKey) return [options.fileKey as T, () => undefined];
|
||||
if (stateCall === 7 && options.snapshot) return [options.snapshot as T, () => undefined];
|
||||
if (stateCall === 8 && options.snapshot) return [options.snapshot as T, () => undefined];
|
||||
return [typeof initial === "function" ? (initial as () => T)() : initial, () => undefined];
|
||||
}
|
||||
};
|
||||
renderPluginPage(react, {
|
||||
context: { serverInstanceId: "server-1", permissions: options.permissions ?? ["server.files.read", "server.files.write", "server.logs.read"] },
|
||||
availability: { available: true, features: [{ key: "config.manage", available: true }] },
|
||||
workspace: {},
|
||||
workspace: options.workspace ?? declaredWorkspace,
|
||||
workspaceActions: {
|
||||
refreshWorkspace: async () => declaredWorkspace,
|
||||
requestFile: async (fileKey: string) => ({ status: "queued", message: fileKey }),
|
||||
getFileSnapshot: async (fileKey: string) => ({ serverInstanceId: "server-1", pluginId: "game.scum", key: fileKey, state: "not-read" }),
|
||||
writeFile: async (fileKey: string) => ({ status: "queued", message: fileKey })
|
||||
|
||||
Reference in New Issue
Block a user