diff --git a/apps/app/src/components/plugin/PluginNavSidebarItems.test.tsx b/apps/app/src/components/plugin/PluginNavSidebarItems.test.tsx index 55303fce25..e9a9a01aa1 100644 --- a/apps/app/src/components/plugin/PluginNavSidebarItems.test.tsx +++ b/apps/app/src/components/plugin/PluginNavSidebarItems.test.tsx @@ -6,11 +6,18 @@ import { render, screen, waitFor, + within, } from "@testing-library/react"; -import { useEffect, type ComponentType } from "react"; +import { + useEffect, + useState, + type ComponentType, + type MouseEvent as ReactMouseEvent, +} from "react"; import { createStore, Provider } from "jotai"; -import { MemoryRouter } from "react-router-dom"; +import { MemoryRouter, useLocation } from "react-router-dom"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { CompactViewportOverrideProvider } from "@bb/shared-ui/hooks/use-compact-viewport"; import { SidebarProvider } from "@/components/ui/sidebar.js"; import { @@ -18,12 +25,25 @@ import { setPluginSlotRegistrations, type PluginRegistrationSet, } from "@/lib/plugin-slots"; +import { + AUTOMATIONS_PLUGIN_ID, + getPluginPanelRoutePath, +} from "@/lib/route-paths"; import { resetAllCrashedPluginSlotsForTest, resetCrashedPluginSlots, } from "./PluginSlotMount"; -import { PluginNavSidebarItems } from "./PluginNavSidebarItems"; -import { pluginNavPanelOrderAtom } from "./pluginNavSidebarAtoms"; +import { + type BuiltInSidebarNavEntry, + ExtensionsNavSidebarItem, + PluginNavSidebarItems, +} from "./PluginNavSidebarItems"; +import { + pluginNavPanelOrderAtom, + pluginNavVisiblePanelKeysAtom, +} from "./pluginNavSidebarAtoms"; +import { splitLayoutAtom } from "@/lib/split-layout/atoms"; +import { countPanes, findPaneByContent } from "@/lib/split-layout"; function registrationSet( overrides: Partial, @@ -66,39 +86,125 @@ function registerPanel( ); } -function renderSidebarItems( - options: { - toolsRoutePath?: string; - storedOrder?: string[]; - compactViewport?: boolean; - } = {}, -) { +interface RenderSidebarItemsOptions { + builtInEntries?: readonly BuiltInSidebarNavEntry[]; + storedOrder?: string[]; + storedVisibleKeys?: string[] | null; + compactViewport?: boolean; + compactCustomizeMode?: boolean; + onCompactCustomizeModeChange?: (isCustomizing: boolean) => void; + splitEnabled?: boolean; +} + +function PluginNavSidebarItemsHarness({ + options, +}: { + options: RenderSidebarItemsOptions; +}) { + const [compactCustomizeMode, setCompactCustomizeMode] = useState( + options.compactCustomizeMode ?? false, + ); + const compactControlProps = options.compactViewport + ? { + compactCustomizeMode, + onCompactCustomizeModeChange: (isCustomizing: boolean) => { + setCompactCustomizeMode(isCustomizing); + options.onCompactCustomizeModeChange?.(isCustomizing); + }, + } + : {}; + + return ( + + ); +} + +function renderSidebarItems(options: RenderSidebarItemsOptions = {}) { const store = createStore(); + const queryClient = new QueryClient({ + defaultOptions: { queries: { retry: false } }, + }); if (options.storedOrder) { store.set(pluginNavPanelOrderAtom, options.storedOrder); } - return render( + if ("storedVisibleKeys" in options) { + store.set( + pluginNavVisiblePanelKeysAtom, + options.storedVisibleKeys ?? null, + ); + } + if (options.splitEnabled) { + store.set(splitLayoutAtom, { + root: { + type: "pane", + paneId: "pane-1", + content: { kind: "new-thread" }, + }, + focusedPaneId: "pane-1", + }); + } + const view = render( - - - - - - - + + + + + + + + + + , ); + return { ...view, store }; } -const ROW_LABELS = new Set(["Extensions", "Docs", "GitHub"]); +function LocationProbe() { + return {useLocation().pathname}; +} + +function panelRowNames(labels: readonly string[] = ["Docs", "GitHub"]): string[] { + const rowLabels = new Set(labels); + const container = screen.queryByTestId("plugin-nav-sidebar-items"); + if (!container) return []; + return Array.from( + container.querySelectorAll("[data-sidebar-navigation-item]"), + ) + .map((row) => row.textContent?.trim() ?? "") + .filter((label) => rowLabels.has(label)); +} -function panelRowNames(): string[] { - return screen - .getAllByRole("button") - .map((button) => button.textContent?.trim() ?? "") - .filter((label) => ROW_LABELS.has(label)); +function builtInEntry( + id: string, + title: string, + onActivate: ( + event: ReactMouseEvent, + ) => void = vi.fn(), +): BuiltInSidebarNavEntry { + return { + kind: "built-in", + pluginId: "__bb__", + id, + title, + icon: