/** * #46 — the project tab bar's "+" add-project button. It must be present, * enabled and visibly a button in EVERY state (notably with zero open projects, * where it is the only way to add a project as a tab), and clicking it opens the * Projects panel. The visibility regression it guards against: the button used * to render with the `ghost` variant (no border/background, muted text) so it * read as faint text rather than a control. */ import { describe, it, expect, vi } from "vitest"; import { render, screen, fireEvent } from "@testing-library/react"; import type { TabItem } from "@/shared"; import { ProjectTabs } from "./ProjectTabs"; const TABS: TabItem[] = [ { id: "a", label: "alpha" }, { id: "b", label: "beta" }, ]; function renderTabs(props: Partial> = {}) { const onOpenProjectsPanel = vi.fn(); render( , ); return { onOpenProjectsPanel }; } describe("ProjectTabs add-project button (#46)", () => { it("renders the + button when projects are open", () => { renderTabs({ items: TABS }); const btn = screen.getByRole("button", { name: "open projects panel" }); expect(btn).toBeTruthy(); expect(btn.textContent).toContain("+"); expect((btn as HTMLButtonElement).disabled).toBe(false); }); it("still renders the + button with ZERO open projects (only way to add one)", () => { renderTabs({ items: [], activeTabId: null }); // The empty placeholder is shown… expect(screen.getByText("No open tabs.")).toBeTruthy(); // …and the add-project button is still present and enabled. const btn = screen.getByRole("button", { name: "open projects panel" }); expect(btn).toBeTruthy(); expect((btn as HTMLButtonElement).disabled).toBe(false); }); it("is a visible bordered control, not the near-invisible ghost glyph", () => { // Regression guard for #46: the secondary variant carries a border; the old // ghost variant did not. Asserting the border class keeps the button from // silently reverting to the invisible style. renderTabs({ items: [] }); const btn = screen.getByRole("button", { name: "open projects panel" }); expect(btn.className).toContain("border"); expect(btn.className).not.toContain("text-muted"); }); it("reflects the panel-open state via aria-pressed", () => { const { onOpenProjectsPanel } = renderTabs({ projectsPanelOpen: true }); const btn = screen.getByRole("button", { name: "open projects panel" }); expect(btn.getAttribute("aria-pressed")).toBe("true"); expect(onOpenProjectsPanel).not.toHaveBeenCalled(); }); it("opens the Projects panel on click, independent of any active project", () => { const { onOpenProjectsPanel } = renderTabs({ items: [], activeTabId: null }); fireEvent.click(screen.getByRole("button", { name: "open projects panel" })); expect(onOpenProjectsPanel).toHaveBeenCalledTimes(1); }); });