Files
agent-radar 2ed3415fb7
Publish Claude Code Binaries / Stage and publish binaries (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-arm64 (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build linux-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build windows-amd64 (push) Canceled after 0s
Sync Tutti Agent Skills / sync (push) Canceled after 0s
Windows Agent Adapters / Test Windows agent adapters (push) Canceled after 0s
Windows Daemon Adapters / Test Windows daemon adapters (push) Canceled after 0s
Publish Tutti App Runtime / Publish runtime catalog (push) Canceled after 0s
源码快照 @ 2026-09-01 (source-only, 剔除非源码素材)
2026-09-01 20:17:27 +08:00

425 lines
12 KiB
TypeScript

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<typeof import("./i18n/index")>("./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 (
<>
<div data-testid="agent-gui-language-probe">
{t("agentHost.agentGui.newConversation")}
</div>
<div data-testid="agent-gui-runtime-probe">
{activityRuntime ? "provided" : "missing"}
</div>
<div data-testid="workspace-workflow-runtime-probe">
{workflowRuntime ? "provided" : "missing"}
</div>
<div data-testid="agent-gui-disabled-suggestions-probe">
{JSON.stringify(
props.hostCapabilities.disabledHomeSuggestions ?? []
)}
</div>
<div data-testid="agent-gui-directory-targets-probe">
{JSON.stringify(
props.hostCapabilities.agentTargets?.map(
(target) => target.agentTargetId
) ?? []
)}
</div>
<div data-testid="agent-gui-handoff-targets-probe">
{JSON.stringify(
props.hostCapabilities.handoffAgentTargets?.map(
(target) => target.agentTargetId
) ?? []
)}
</div>
<div data-testid="agent-gui-mention-targets-probe">
{JSON.stringify(props.hostCapabilities.mentionAgentTargets ?? [])}
</div>
<Tooltip>
<TooltipTrigger asChild>
<button type="button">tooltip probe</button>
</TooltipTrigger>
<TooltipContent>AgentGUI tooltip probe</TooltipContent>
</Tooltip>
</>
);
}
};
});
describe("AgentGUI i18n", () => {
beforeEach(() => {
agentGuiNodeSpy.mockClear();
});
it("rerenders agent copy when the host locale changes", () => {
const { rerender } = render(<AgentGUI {...createAgentGUIProps("en")} />);
expect(screen.getByTestId("agent-gui-language-probe")).toHaveTextContent(
"New session"
);
rerender(<AgentGUI {...createAgentGUIProps("zh-CN")} />);
expect(screen.getByTestId("agent-gui-language-probe")).toHaveTextContent(
"新建会话"
);
});
it("uses the host locale when mounted", () => {
render(<AgentGUI {...createAgentGUIProps("zh-CN")} />);
expect(screen.getByTestId("agent-gui-language-probe")).toHaveTextContent(
"新建会话"
);
});
it("provides the required activity runtime to the AgentGUI node", () => {
render(
<AgentGUI
{...createAgentGUIProps("en")}
agentActivityRuntime={{} as AgentGUIProps["agentActivityRuntime"]}
/>
);
expect(screen.getByTestId("agent-gui-runtime-probe")).toHaveTextContent(
"provided"
);
});
it("provides the optional workspace workflow runtime to the AgentGUI node", () => {
render(
<AgentGUI
{...createAgentGUIProps("en")}
tuttiModePlanReviewRuntime={
{} as AgentGUIProps["tuttiModePlanReviewRuntime"]
}
/>
);
expect(
screen.getByTestId("workspace-workflow-runtime-probe")
).toHaveTextContent("provided");
});
it("forwards disabled home suggestions to the internal node", () => {
render(
<AgentGUI
{...createAgentGUIProps("en")}
disabled={["meet-tutti", "import-session"]}
/>
);
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(
<AgentGUI
{...createAgentGUIProps("en")}
agentDirectory={{
agents: [agent("local-codex", "codex")],
capturedAtUnixMs: null,
error: null,
status: "ready"
}}
handoffAgentDirectory={{
agents: [
agent("local-codex", "codex"),
agent("shared-agent:claude", "claude-code")
],
capturedAtUnixMs: null,
error: null,
status: "ready"
}}
/>
);
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(
<AgentGUI
{...createAgentGUIProps("en")}
agentDirectory={{
agents: [
{
...agent("extension-kimi", "kimi-code"),
availability: { status: "unavailable", reason: "probe_failed" },
setupKind: "target_runtime"
}
],
capturedAtUnixMs: null,
error: null,
status: "ready"
}}
/>
);
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(
<AgentGUI
{...baseProps}
agentDirectory={{
agents: [agent("extension-kimi", "kimi-code")],
capturedAtUnixMs: null,
error: null,
status: "ready"
}}
hostCapabilities={{
...baseProps.hostCapabilities,
providerReadinessGates: {
"kimi-code": { status: "checking" }
}
}}
/>
);
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(
<AgentGUI
{...createAgentGUIProps("en")}
agentDirectory={{
agents: [agent("local-codex", "codex")],
capturedAtUnixMs: null,
error: null,
status: "ready"
}}
handoffAgentDirectory={{
agents: [agent("local-codex", "codex")],
capturedAtUnixMs: null,
error: null,
status: "ready"
}}
mentionAgentDirectory={{
agents: [
{
...agent("shared-agent:offline-codex", "codex"),
availability: { status: "unavailable", reason: "owner_offline" }
}
],
capturedAtUnixMs: null,
error: null,
status: "ready"
}}
/>
);
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(
<AgentGUI
{...createAgentGUIProps("en")}
hostCapabilities={{ showHandoffTargetOwnershipLabels: true }}
/>
);
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(<AgentGUI {...props} />);
const first = agentGuiNodeSpy.mock.calls.at(-1)?.[0] as
| AgentGUINodeProbeProps
| undefined;
rerender(
<AgentGUI
{...props}
frame={{ ...props.frame, width: 960, height: 640 }}
/>
);
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(<AgentGUI {...props} />);
const first = agentGuiNodeSpy.mock.calls.at(-1)?.[0] as
| AgentGUINodeProbeProps
| undefined;
rerender(
<AgentGUI
{...props}
agentDirectory={{
...props.agentDirectory,
agents: [
{
...firstAgent,
availability: { status: "auth_required" as const }
}
]
}}
/>
);
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
};
}