/** @vitest-environment jsdom */ import { act } from "react"; import { createRoot, type Root } from "react-dom/client"; import { afterEach, describe, expect, it, vi } from "vitest"; import type { GamePluginResponse } from "../api/types"; import { defaultServerCreateForm } from "../contracts/serverManagement"; import { minimalServerCreateRequestFromForm } from "../schemas/serverManagement"; import { ServerDeploymentWorkflow } from "./ServerDeploymentWorkflow"; const plugin: GamePluginResponse = { id: "game.runtime", name: "Runtime Game", version: "1.0.0", serverType: "runtime", manifestRef: "artifact://runtime-manifest", createFormSchemaRef: "schemas/create.json", createFields: [{ key: "serverRoot", label: "服务器目录", type: "text", required: true }], requiredRunCapabilities: ["process.install"], declaredPermissions: ["server.create"], permissions: { ai: false, logs: true, files: false, jobs: true, artifacts: false }, lifecycleActions: { install: "actions/install.json", start: "actions/start.json", stop: "actions/stop.json" }, bridgeActions: [], pages: [], tags: [], aiPurposes: [], productionLifecycle: { operations: ["install"], dependencyPolicy: "optional", approvalRequired: [] }, status: "installed", runtimeProfiles: { transportProfiles: [{ key: "rcon", kind: "rcon", targetKey: "rcon.password", capabilities: ["remote.run.rcon.command"] }], lifecycleProfiles: [{ key: "local", mode: "local-process", capabilities: ["process.install"], transportKeys: ["rcon"] }] } }; let root: Root | null = null; let container: HTMLDivElement | null = null; (globalThis as typeof globalThis & { IS_REACT_ACT_ENVIRONMENT: boolean }).IS_REACT_ACT_ENVIRONMENT = true; afterEach(async () => { if (root) { await act(async () => root?.unmount()); } container?.remove(); root = null; container = null; }); describe("ServerDeploymentWorkflow", () => { it("submits plugin type and server name as a minimal create request", async () => { container = document.createElement("div"); document.body.append(container); root = createRoot(container); const initialForm = defaultServerCreateForm([plugin], []); let submitted: ReturnType | undefined; const onSubmit = vi.fn(async (form: typeof initialForm) => { submitted = minimalServerCreateRequestFromForm(form, 17); }); await act(async () => { root?.render( undefined} onSubmit={onSubmit} /> ); }); expect(container.querySelector('select[name="pluginId"]')).not.toBeNull(); expect(container.querySelector('input[name="name"]')).not.toBeNull(); for (const field of ["deploymentTargetId", "runEndpointId", "profileKey", "serverRoot", "startCommand"]) { expect(container.querySelector(`[name="${field}"]`)).toBeNull(); } expect(container.textContent).not.toContain("运行连接设置"); expect(container.querySelector('select[name="deploymentMode"]')).toBeNull(); const nameInput = container.querySelector('input[name="name"]'); if (!nameInput) throw new Error("server name input not found"); await act(async () => { setInputValue(nameInput, "Minimal Runtime Server"); }); await submitWorkflow(container); expect(container.textContent).toContain("本次只创建服务器记录"); expect(container.textContent).toContain("Minimal Runtime Server"); await submitWorkflow(container); expect(onSubmit).toHaveBeenCalledTimes(1); expect(submitted).toEqual({ id: "server-minimal-runtime-server-17", pluginId: "game.runtime", name: "Minimal Runtime Server", idempotencyKey: "web:create:server-minimal-runtime-server-17:17" }); }); }); async function submitWorkflow(target: HTMLElement) { const form = target.querySelector('form[aria-label="创建服务器部署向导"]'); if (!form) throw new Error("create workflow form not found"); await act(async () => { form.dispatchEvent(new SubmitEvent("submit", { bubbles: true, cancelable: true })); }); } function setInputValue(input: HTMLInputElement, value: string) { const setter = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, "value")?.set; setter?.call(input, value); input.dispatchEvent(new Event("input", { bubbles: true })); }