import "@testing-library/jest-dom/vitest"; import { render, screen } from "@testing-library/react"; import { beforeEach, describe, expect, it, vi } from "vitest"; import { AgentGUI, type AgentGUIProps } from "./AgentGUI"; const { agentGuiNodeSpy } = vi.hoisted(() => ({ agentGuiNodeSpy: vi.fn() })); interface AgentGUINodeProbeProps { hostCapabilities: { agentTargets?: readonly { agentTargetId?: string; availability: unknown; disabled?: boolean; ref: unknown; }[]; disabledHomeSuggestions?: readonly string[]; handoffAgentTargets?: readonly { agentTargetId?: string; availability: unknown; disabled?: boolean; ref: unknown; }[]; mentionAgentTargets?: readonly { agentTargetId?: string; disabled?: boolean; iconUrl?: string; }[]; showHandoffTargetOwnershipLabels?: boolean; }; } function createAgentGUIProps(locale: AgentGUIProps["locale"]): AgentGUIProps { return { agentDirectory: { agents: [], capturedAtUnixMs: null, error: null, status: "ready" }, locale, frame: {} } as unknown as AgentGUIProps; } vi.mock("./agent-gui/agentGuiNode/AgentGUINode", async () => { const { useTranslation } = await vi.importActual("./i18n/index"); const { useOptionalAgentGUIRuntime } = await vi.importActual< typeof import("./agentActivityRuntime") >("./agentActivityRuntime"); const { useOptionalTuttiModePlanReviewRuntime } = await vi.importActual< typeof import("./workspaceWorkflow") >("./workspaceWorkflow"); const { Tooltip, TooltipContent, TooltipTrigger } = await vi.importActual< typeof import("@tutti-os/ui-system") >("@tutti-os/ui-system"); return { AgentGUINode: (props: AgentGUINodeProbeProps) => { agentGuiNodeSpy(props); const { t } = useTranslation(); const activityRuntime = useOptionalAgentGUIRuntime(); const workflowRuntime = useOptionalTuttiModePlanReviewRuntime(); return ( <>
{t("agentHost.agentGui.newConversation")}
{activityRuntime ? "provided" : "missing"}
{workflowRuntime ? "provided" : "missing"}
{JSON.stringify( props.hostCapabilities.disabledHomeSuggestions ?? [] )}
{JSON.stringify( props.hostCapabilities.agentTargets?.map( (target) => target.agentTargetId ) ?? [] )}
{JSON.stringify( props.hostCapabilities.handoffAgentTargets?.map( (target) => target.agentTargetId ) ?? [] )}
{JSON.stringify(props.hostCapabilities.mentionAgentTargets ?? [])}
AgentGUI tooltip probe ); } }; }); describe("AgentGUI i18n", () => { beforeEach(() => { agentGuiNodeSpy.mockClear(); }); it("rerenders agent copy when the host locale changes", () => { const { rerender } = render(); expect(screen.getByTestId("agent-gui-language-probe")).toHaveTextContent( "New session" ); rerender(); expect(screen.getByTestId("agent-gui-language-probe")).toHaveTextContent( "新建会话" ); }); it("uses the host locale when mounted", () => { render(); expect(screen.getByTestId("agent-gui-language-probe")).toHaveTextContent( "新建会话" ); }); it("provides the required activity runtime to the AgentGUI node", () => { render( ); expect(screen.getByTestId("agent-gui-runtime-probe")).toHaveTextContent( "provided" ); }); it("provides the optional workspace workflow runtime to the AgentGUI node", () => { render( ); expect( screen.getByTestId("workspace-workflow-runtime-probe") ).toHaveTextContent("provided"); }); it("forwards disabled home suggestions to the internal node", () => { render( ); expect( screen.getByTestId("agent-gui-disabled-suggestions-probe") ).toHaveTextContent('["meet-tutti","import-session"]'); }); it("keeps the runtime directory separate from the handoff launch directory", () => { render( ); expect( screen.getByTestId("agent-gui-directory-targets-probe") ).toHaveTextContent('["local-codex"]'); expect( screen.getByTestId("agent-gui-handoff-targets-probe") ).toHaveTextContent('["local-codex","shared-agent:claude"]'); }); it("disables unavailable setup targets only in the handoff directory", () => { render( ); const props = agentGuiNodeSpy.mock.lastCall?.[0] as | AgentGUINodeProbeProps | undefined; expect(props?.hostCapabilities.agentTargets?.[0]?.disabled).toBeUndefined(); expect(props?.hostCapabilities.handoffAgentTargets?.[0]?.disabled).toBe( true ); }); it("disables handoff targets while their provider readiness check is active", () => { const baseProps = createAgentGUIProps("en"); render( ); const props = agentGuiNodeSpy.mock.lastCall?.[0] as | AgentGUINodeProbeProps | undefined; expect(props?.hostCapabilities.agentTargets?.[0]?.disabled).toBeUndefined(); expect(props?.hostCapabilities.handoffAgentTargets?.[0]?.disabled).toBe( true ); }); it("keeps unavailable mention identities outside action directories", () => { render( ); expect( screen.getByTestId("agent-gui-directory-targets-probe") ).toHaveTextContent('["local-codex"]'); expect( screen.getByTestId("agent-gui-handoff-targets-probe") ).toHaveTextContent('["local-codex"]'); expect( screen.getByTestId("agent-gui-mention-targets-probe") ).toHaveTextContent('"agentTargetId":"shared-agent:offline-codex"'); expect( screen.getByTestId("agent-gui-mention-targets-probe") ).toHaveTextContent('"disabled":true'); expect( screen.getByTestId("agent-gui-mention-targets-probe") ).toHaveTextContent('"iconUrl":"/shared-agent:offline-codex.png"'); }); it("forwards the host-owned handoff ownership presentation", () => { render( ); const props = agentGuiNodeSpy.mock.calls.at(-1)?.[0] as | AgentGUINodeProbeProps | undefined; expect(props?.hostCapabilities.showHandoffTargetOwnershipLabels).toBe(true); }); it("reuses agent target projections across frame-only renders", () => { const agents = [agent("local-codex", "codex")]; const props = { ...createAgentGUIProps("en"), agentDirectory: { agents, capturedAtUnixMs: null, error: null, status: "ready" as const } }; const { rerender } = render(); const first = agentGuiNodeSpy.mock.calls.at(-1)?.[0] as | AgentGUINodeProbeProps | undefined; rerender( ); const second = agentGuiNodeSpy.mock.calls.at(-1)?.[0] as | AgentGUINodeProbeProps | undefined; expect(second?.hostCapabilities.agentTargets).toBe( first?.hostCapabilities.agentTargets ); expect(second?.hostCapabilities.handoffAgentTargets).toBe( first?.hostCapabilities.handoffAgentTargets ); expect(second?.hostCapabilities.handoffAgentTargets).toBe( second?.hostCapabilities.agentTargets ); expect(second?.hostCapabilities.agentTargets?.[0]?.availability).toBe( first?.hostCapabilities.agentTargets?.[0]?.availability ); expect(second?.hostCapabilities.agentTargets?.[0]?.ref).toBe( first?.hostCapabilities.agentTargets?.[0]?.ref ); }); it("rebuilds agent target projections when the directory changes", () => { const firstAgent = agent("local-codex", "codex"); const props = { ...createAgentGUIProps("en"), agentDirectory: { agents: [firstAgent], capturedAtUnixMs: null, error: null, status: "ready" as const } }; const { rerender } = render(); const first = agentGuiNodeSpy.mock.calls.at(-1)?.[0] as | AgentGUINodeProbeProps | undefined; rerender( ); const second = agentGuiNodeSpy.mock.calls.at(-1)?.[0] as | AgentGUINodeProbeProps | undefined; expect(second?.hostCapabilities.agentTargets).not.toBe( first?.hostCapabilities.agentTargets ); expect(second?.hostCapabilities.agentTargets?.[0]?.availability).not.toBe( first?.hostCapabilities.agentTargets?.[0]?.availability ); expect(second?.hostCapabilities.agentTargets?.[0]?.ref).not.toBe( first?.hostCapabilities.agentTargets?.[0]?.ref ); }); }); function agent(agentTargetId: string, provider: string) { return { agentTargetId, name: agentTargetId, iconUrl: `/${agentTargetId}.png`, availability: { status: "ready" as const }, provider }; }