import { describe, it, expect, vi, beforeEach } from "vitest";
import { render, screen, fireEvent, waitFor } from "@testing-library/react";
import { ProjectSelector } from "../ProjectSelector";
import type { ProjectInfo } from "@fusion/core";
// Mock lucide-react
vi.mock("lucide-react", async () => {
const actual = await vi.importActual("lucide-react");
return {
...actual,
ChevronDown: () => ▼,
Check: () => ✓,
Folder: () => 📁,
Grid3X3: () => ⊞,
Search: () => 🔍,
Clock: () => 🕐,
X: () => ✕,
Play: () => ▶,
Pause: () => ⏸,
AlertCircle: () => ⚠,
Loader2: () => ⟳,
};
});
function makeProject(overrides: Partial = {}): ProjectInfo {
return {
id: "proj_abc123",
name: "Test Project",
path: "/home/user/projects/test",
status: "active",
isolationMode: "in-process",
createdAt: "2026-01-01T00:00:00.000Z",
updatedAt: "2026-01-01T00:00:00.000Z",
lastActivityAt: new Date().toISOString(),
...overrides,
};
}
const noop = () => {};
describe("ProjectSelector", () => {
beforeEach(() => {
vi.clearAllMocks();
});
it("renders without crashing", () => {
render(
);
expect(screen.getByTestId("project-selector-trigger")).toBeDefined();
});
it("does not render when only one project exists", () => {
const { container } = render(
);
expect(container.firstChild).toBeNull();
});
it("does not render when no projects exist", () => {
const { container } = render(
);
expect(container.firstChild).toBeNull();
});
it("shows current project name in trigger", () => {
render(
);
expect(screen.getByText("Project One")).toBeDefined();
});
it("opens dropdown on click", () => {
render(
);
fireEvent.click(screen.getByTestId("project-selector-trigger"));
expect(screen.getByTestId("project-selector-dropdown")).toBeDefined();
});
it("shows all projects in dropdown", () => {
render(
);
fireEvent.click(screen.getByTestId("project-selector-trigger"));
expect(screen.getByText("Project Two")).toBeDefined();
});
it("calls onSelect when project is clicked", () => {
const onSelect = vi.fn();
const projectTwo = makeProject({ id: "proj_2", name: "Project Two" });
render(
);
fireEvent.click(screen.getByTestId("project-selector-trigger"));
fireEvent.click(screen.getByText("Project Two"));
expect(onSelect).toHaveBeenCalledWith(projectTwo);
});
it("closes dropdown after selection", () => {
const onSelect = vi.fn();
render(
);
fireEvent.click(screen.getByTestId("project-selector-trigger"));
fireEvent.click(screen.getByText("Project Two"));
expect(screen.queryByTestId("project-selector-dropdown")).toBeNull();
});
it("calls onViewAll when 'View All Projects' is clicked", () => {
const onViewAll = vi.fn();
render(
);
fireEvent.click(screen.getByTestId("project-selector-trigger"));
fireEvent.click(screen.getByText("View All Projects"));
expect(onViewAll).toHaveBeenCalled();
});
it("shows search input when 5+ projects", () => {
render(
makeProject({ id: `proj_${i}`, name: `Project ${i}` })
)}
currentProject={makeProject({ id: "proj_0" })}
onSelect={noop}
onViewAll={noop}
/>
);
fireEvent.click(screen.getByTestId("project-selector-trigger"));
expect(screen.getByPlaceholderText("Search projects...")).toBeDefined();
});
it("does not show search input when fewer than 5 projects", () => {
render(
);
fireEvent.click(screen.getByTestId("project-selector-trigger"));
expect(screen.queryByPlaceholderText("Search projects...")).toBeNull();
});
it("filters projects based on search query", () => {
render(
makeProject({ id: `proj_${i}`, name: `Project ${i}` })
)}
currentProject={makeProject({ id: "proj_0" })}
onSelect={noop}
onViewAll={noop}
/>
);
fireEvent.click(screen.getByTestId("project-selector-trigger"));
const searchInput = screen.getByPlaceholderText("Search projects...");
fireEvent.change(searchInput, { target: { value: "Project 2" } });
expect(screen.getByText("Project 2")).toBeDefined();
expect(screen.queryByText("Project 1")).toBeNull();
});
it("shows recent projects section", () => {
render(
);
fireEvent.click(screen.getByTestId("project-selector-trigger"));
expect(screen.getByText("Recent")).toBeDefined();
expect(screen.getByText("Project Two")).toBeDefined();
});
it("closes dropdown on escape key", () => {
render(
);
fireEvent.click(screen.getByTestId("project-selector-trigger"));
expect(screen.getByTestId("project-selector-dropdown")).toBeDefined();
fireEvent.keyDown(document, { key: "Escape" });
expect(screen.queryByTestId("project-selector-dropdown")).toBeNull();
});
it("closes dropdown on outside click", () => {
render(
<>
Outside element
>
);
fireEvent.click(screen.getByTestId("project-selector-trigger"));
expect(screen.getByTestId("project-selector-dropdown")).toBeDefined();
fireEvent.mouseDown(screen.getByTestId("outside"));
expect(screen.queryByTestId("project-selector-dropdown")).toBeNull();
});
it("trigger has correct aria attributes", () => {
render(
);
const trigger = screen.getByTestId("project-selector-trigger");
expect(trigger.getAttribute("aria-haspopup")).toBe("listbox");
expect(trigger.getAttribute("aria-expanded")).toBe("false");
fireEvent.click(trigger);
expect(trigger.getAttribute("aria-expanded")).toBe("true");
});
it("shows checkmark for current project", () => {
render(
);
fireEvent.click(screen.getByTestId("project-selector-trigger"));
// Should have checkmark for current project (though in dropdown it might not be visible due to filtering)
});
});