import { createEvent, fireEvent, render, screen } from "@testing-library/react"; import { TooltipProvider } from "@tutti-os/ui-system"; import { beforeEach, describe, expect, it, vi } from "vitest"; import type { AgentGUIAgentTarget } from "../../../types"; import { AgentTargetInfoRendererProvider } from "../../../shared/AgentTargetInfoRendererContext"; import { AGENT_GUI_PROVIDER_RAIL_PREFERENCES_STORAGE_KEY, parseAgentGUIProviderRailPreferences } from "../model/agentGuiProviderRailOrder"; import type { AgentGUIViewLabels } from "./AgentGUINodeView.types"; import { AgentGUIProviderRail } from "./AgentGUIProviderRail"; describe("AgentGUIProviderRail selection", () => { beforeEach(() => { globalThis.localStorage.clear(); }); it("derives mutually exclusive aria and visual selection only from conversationFilter", () => { const readyTarget = target({ agentTargetId: "agent:ready", label: "Ready Agent", provider: "ready-provider", targetId: "legacy:ready" }); const unavailableTarget = target({ agentTargetId: "agent:offline", availability: { status: "unavailable" }, disabled: true, label: "Offline Agent", provider: "offline-provider", targetId: "legacy:offline" }); const placeholderTarget = target({ agentTargetId: "agent:placeholder", disabled: true, label: "Placeholder Agent", provider: "placeholder-provider", targetId: "local:placeholder-provider" }); const props = providerRailProps({ agentTargets: [readyTarget, unavailableTarget, placeholderTarget], conversationFilter: { kind: "agentTarget", agentTargetId: "agent:offline" }, selectedAgentTarget: placeholderTarget }); const view = render(providerRail(props)); expectOnlySelectedTab("Offline Agent"); view.rerender( providerRail({ ...props, conversationFilter: { kind: "all" }, selectedAgentTarget: unavailableTarget }) ); expectOnlySelectedTab("All agents"); view.rerender( providerRail({ ...props, conversationFilter: { kind: "agentTarget" as const, agentTargetId: "agent:placeholder" }, selectedAgentTarget: readyTarget }) ); expectOnlySelectedTab("Placeholder Agent"); }); it("uses canonical agentTargetId for tile clicks and fails closed without it", () => { const onSelectConversationFilterTarget = vi.fn(); const canonicalTarget = target({ agentTargetId: " agent:canonical ", disabled: true, label: "Canonical Agent", provider: "canonical-provider", targetId: "legacy-provider-target" }); const compatibilityTarget = target({ label: "Compatibility Agent", provider: "compatibility-provider", targetId: "legacy-only-target" }); render( providerRail( providerRailProps({ agentTargets: [canonicalTarget, compatibilityTarget], onSelectConversationFilterTarget }) ) ); fireEvent.click(screen.getByRole("tab", { name: "Canonical Agent" })); expect(onSelectConversationFilterTarget).toHaveBeenCalledTimes(1); expect(onSelectConversationFilterTarget).toHaveBeenLastCalledWith({ provider: "canonical-provider", agentTargetId: "agent:canonical" }); fireEvent.click(screen.getByRole("tab", { name: "Compatibility Agent" })); expect(onSelectConversationFilterTarget).toHaveBeenCalledTimes(1); }); it("keeps All clicks aggregate and uses canonical identity for manager fallback", () => { const onSelectConversationFilterTarget = vi.fn(); const onSelectHomeComposerAgentTarget = vi.fn(); const onUpdateConversationFilter = vi.fn(); const selectedTarget = target({ agentTargetId: "agent:selected", disabled: true, label: "Selected Agent", provider: "selected-provider", targetId: "legacy:selected" }); const fallbackTarget = target({ agentTargetId: "agent:fallback", label: "Fallback Agent", provider: "fallback-provider", targetId: "legacy:fallback" }); const props = providerRailProps({ agentTargets: [selectedTarget, fallbackTarget], conversationFilter: { kind: "all" }, onSelectConversationFilterTarget, onSelectHomeComposerAgentTarget, onUpdateConversationFilter, selectedAgentTarget: selectedTarget }); const view = render(providerRail(props)); fireEvent.click(screen.getByRole("tab", { name: "All agents" })); expect(onUpdateConversationFilter).toHaveBeenLastCalledWith({ kind: "all" }); expect(onSelectConversationFilterTarget).not.toHaveBeenCalled(); view.rerender( providerRail({ ...props, conversationFilter: { kind: "agentTarget" as const, agentTargetId: "agent:selected" }, managerOpen: true }) ); fireEvent.contextMenu( screen.getByRole("listitem", { name: "Reorder Selected Agent" }) ); fireEvent.click( screen.getByRole("button", { name: "Remove Selected Agent" }) ); expect(onSelectHomeComposerAgentTarget).toHaveBeenCalledWith({ provider: "fallback-provider", agentTargetId: "agent:fallback" }); expect(onUpdateConversationFilter).toHaveBeenLastCalledWith({ kind: "all" }); }); it("resolves rail container drop targets from provider tile identities", () => { const codex = target({ agentTargetId: "agent:codex", label: "Codex", provider: "codex", targetId: "local:codex" }); const claude = target({ agentTargetId: "agent:claude", label: "Claude", provider: "claude-code", targetId: "local:claude" }); const cursor = target({ agentTargetId: "agent:cursor", label: "Cursor", provider: "cursor", targetId: "local:cursor" }); render( providerRail(providerRailProps({ agentTargets: [codex, claude, cursor] })) ); const codexTile = screen.getByRole("tab", { name: "Codex" }); const claudeTile = screen.getByRole("tab", { name: "Claude" }); const cursorTile = screen.getByRole("tab", { name: "Cursor" }); mockVerticalBounds(codexTile, 0); mockVerticalBounds(claudeTile, 40); mockVerticalBounds(cursorTile, 80); const dataTransfer = createDataTransfer(); fireEvent.dragStart(cursorTile, { dataTransfer }); const tablist = screen.getByRole("tablist"); const dragOverEvent = createEvent.dragOver(tablist, { dataTransfer }); Object.defineProperty(dragOverEvent, "clientY", { value: 5 }); fireEvent(tablist, dragOverEvent); expect(codexTile).toHaveAttribute("data-drag-over", "before"); const dropEvent = createEvent.drop(tablist, { dataTransfer }); Object.defineProperty(dropEvent, "clientY", { value: 5 }); fireEvent(tablist, dropEvent); expect( parseAgentGUIProviderRailPreferences( globalThis.localStorage.getItem( AGENT_GUI_PROVIDER_RAIL_PREFERENCES_STORAGE_KEY ) ).order ).toEqual(["local:cursor", "local:codex", "local:claude"]); expect( screen.getAllByRole("tab").map((tab) => tab.getAttribute("aria-label")) ).toEqual(["All agents", "Cursor", "Codex", "Claude"]); }); it("renders a shared owner badge through the Avatar primitive", () => { render( providerRail( providerRailProps({ agentTargets: [ target({ agentTargetId: "agent:shared", badge: { iconUrl: "https://cdn.example.test/jackson.png", label: "Jackson" }, label: "Shared Codex", provider: "codex", targetId: "shared:codex" }) ] }) ) ); const badge = screen .getByRole("tab", { name: "Shared Codex, Jackson" }) .querySelector('[data-agent-owner-badge="true"]'); expect(badge).not.toBeNull(); expect(badge?.querySelector('[data-slot="avatar"]')).not.toBeNull(); }); it("lazily renders exact Host target information for the hovered rail tile", async () => { const sharedTarget = target({ agentTargetId: "agent:shared", label: "Shared Codex", ownerDeviceLabel: "Vector's MacBook Pro", provider: "codex", targetId: "shared:codex" }); const renderAgentTargetInfo = vi.fn(({ surface, target }) => (