// @vitest-environment jsdom import type { ReactNode } from "react"; import { flushSync } from "react-dom"; import { createRoot } from "react-dom/client"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import type { Agent } from "@paperclipai/shared"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { ToastProvider } from "../context/ToastContext"; import { Agents } from "./Agents"; const mockAgentsApi = vi.hoisted(() => ({ list: vi.fn(), org: vi.fn(), })); const mockHeartbeatsApi = vi.hoisted(() => ({ liveRunsForCompany: vi.fn(), })); const mockResourceMembershipsApi = vi.hoisted(() => ({ listMine: vi.fn(), updateAgent: vi.fn(), })); const mockOpenNewAgent = vi.hoisted(() => vi.fn()); const mockSetBreadcrumbs = vi.hoisted(() => vi.fn()); vi.mock("@/lib/router", () => ({ Link: ({ children, to, ...props }: { children: ReactNode; to: string }) => ( {children} ), useLocation: () => ({ pathname: "/agents/all", search: "", hash: "", state: null }), useNavigate: () => vi.fn(), })); vi.mock("../context/CompanyContext", () => ({ useCompany: () => ({ selectedCompanyId: "company-1" }), })); vi.mock("../context/DialogContext", () => ({ useDialogActions: () => ({ openNewAgent: mockOpenNewAgent }), })); vi.mock("../context/BreadcrumbContext", () => ({ useBreadcrumbs: () => ({ setBreadcrumbs: mockSetBreadcrumbs }), })); vi.mock("../context/SidebarContext", () => ({ useSidebar: () => ({ isMobile: false }), })); vi.mock("../api/agents", () => ({ agentsApi: mockAgentsApi, })); vi.mock("../api/heartbeats", () => ({ heartbeatsApi: mockHeartbeatsApi, })); vi.mock("../api/resourceMemberships", () => ({ resourceMembershipsApi: mockResourceMembershipsApi, })); vi.mock("../adapters/adapter-display-registry", () => ({ getAdapterLabel: (type: string) => type, })); // eslint-disable-next-line @typescript-eslint/no-explicit-any (globalThis as any).IS_REACT_ACT_ENVIRONMENT = true; async function act(callback: () => void | Promise) { let result: void | Promise = undefined; flushSync(() => { result = callback(); }); await result; } function makeAgent(overrides: Partial): Agent { return { id: "agent-1", companyId: "company-1", name: "Alpha", urlKey: "alpha", role: "engineer", title: null, icon: null, status: "active", reportsTo: null, capabilities: null, adapterType: "codex_local", adapterConfig: {}, runtimeConfig: {}, budgetMonthlyCents: 0, spentMonthlyCents: 0, pauseReason: null, pausedAt: null, permissions: { canCreateAgents: false }, lastHeartbeatAt: null, metadata: null, createdAt: new Date("2026-01-01T00:00:00Z"), updatedAt: new Date("2026-01-01T00:00:00Z"), ...overrides, }; } async function flushReact() { await act(async () => { await Promise.resolve(); await new Promise((resolve) => window.setTimeout(resolve, 0)); }); } describe("Agents", () => { let container: HTMLDivElement; let root: ReturnType | null; let queryClient: QueryClient; beforeEach(() => { container = document.createElement("div"); document.body.appendChild(container); root = null; queryClient = new QueryClient({ defaultOptions: { queries: { retry: false } }, }); mockAgentsApi.list.mockResolvedValue([ makeAgent({ adapterConfig: { model: "gpt-5.4" } }), ]); mockAgentsApi.org.mockResolvedValue([ { id: "agent-1", name: "Alpha", role: "engineer", status: "active", reports: [], }, ]); mockHeartbeatsApi.liveRunsForCompany.mockResolvedValue([]); mockResourceMembershipsApi.listMine.mockResolvedValue({ projectMemberships: {}, agentMemberships: {}, updatedAt: null, }); mockResourceMembershipsApi.updateAgent.mockResolvedValue({ resourceType: "agent", resourceId: "agent-1", state: "left", updatedAt: new Date("2026-01-02T00:00:00Z"), }); }); afterEach(async () => { const currentRoot = root; if (currentRoot) { await act(async () => { currentRoot.unmount(); }); } queryClient.clear(); container.remove(); document.body.innerHTML = ""; vi.clearAllMocks(); }); it("shows the configured model beside the adapter on the all agents page", async () => { root = createRoot(container); await act(async () => { root!.render( , ); }); await flushReact(); await flushReact(); expect(container.textContent).toContain("codex_local"); expect(container.textContent).toContain("gpt-5.4"); }); });