// @vitest-environment jsdom import { act } from "react"; import type { ReactNode } from "react"; import { createRoot } from "react-dom/client"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { Sidebar } from "./Sidebar"; const mockHeartbeatsApi = vi.hoisted(() => ({ liveRunsForCompany: vi.fn(), })); const mockInstanceSettingsApi = vi.hoisted(() => ({ getExperimental: vi.fn(), })); vi.mock("@/lib/router", () => ({ NavLink: ({ to, children, className, ...props }: { to: string; children: ReactNode; className?: string | ((state: { isActive: boolean }) => string); }) => ( {children} ), })); vi.mock("../context/DialogContext", () => ({ useDialog: () => ({ openNewIssue: vi.fn(), }), })); vi.mock("../context/CompanyContext", () => ({ useCompany: () => ({ selectedCompanyId: "company-1", selectedCompany: { id: "company-1", issuePrefix: "PAP", name: "Paperclip" }, }), })); vi.mock("../context/SidebarContext", () => ({ useSidebar: () => ({ isMobile: false, setSidebarOpen: vi.fn(), }), })); vi.mock("../api/heartbeats", () => ({ heartbeatsApi: mockHeartbeatsApi, })); vi.mock("../api/instanceSettings", () => ({ instanceSettingsApi: mockInstanceSettingsApi, })); vi.mock("../hooks/useInboxBadge", () => ({ useInboxBadge: () => ({ inbox: 0, failedRuns: 0 }), })); vi.mock("@/plugins/slots", () => ({ PluginSlotOutlet: () => null, })); vi.mock("./SidebarCompanyMenu", () => ({ SidebarCompanyMenu: () =>
Company menu
, })); vi.mock("./SidebarProjects", () => ({ SidebarProjects: () => null, })); vi.mock("./SidebarAgents", () => ({ SidebarAgents: () => null, })); // eslint-disable-next-line @typescript-eslint/no-explicit-any (globalThis as any).IS_REACT_ACT_ENVIRONMENT = true; async function flushReact() { await act(async () => { await Promise.resolve(); await new Promise((resolve) => window.setTimeout(resolve, 0)); }); } describe("Sidebar", () => { let container: HTMLDivElement; beforeEach(() => { container = document.createElement("div"); document.body.appendChild(container); mockHeartbeatsApi.liveRunsForCompany.mockResolvedValue([]); }); afterEach(() => { container.remove(); document.body.innerHTML = ""; vi.clearAllMocks(); }); it("does not flash the Workspaces link while experimental settings are loading", async () => { mockInstanceSettingsApi.getExperimental.mockImplementation(() => new Promise(() => {})); const root = createRoot(container); const queryClient = new QueryClient({ defaultOptions: { queries: { retry: false } }, }); await act(async () => { root.render( , ); }); await flushReact(); expect(container.textContent).not.toContain("Workspaces"); await act(async () => { root.unmount(); }); }); it("shows the Workspaces link when isolated workspaces are enabled", async () => { mockInstanceSettingsApi.getExperimental.mockResolvedValue({ enableIsolatedWorkspaces: true }); const root = createRoot(container); const queryClient = new QueryClient({ defaultOptions: { queries: { retry: false } }, }); await act(async () => { root.render( , ); }); await flushReact(); const link = [...container.querySelectorAll("a")].find((anchor) => anchor.textContent === "Workspaces"); expect(link?.getAttribute("href")).toBe("/workspaces"); await act(async () => { root.unmount(); }); }); });