import { describe, it, expect, vi, beforeEach } from "vitest"; import { render, screen, act, waitFor } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { AppShell } from "@/components/AppShell"; import { useNavStore } from "@/stores/nav-store"; import { useFocusStore } from "@/stores/focus-store"; import { useCaptureStore } from "@/stores/capture-store"; import { useInboxStore } from "@/stores/inbox-store"; import { simulateEvent, resetMocks, setMockResponse } from "../mocks/tauri-api"; import { makeFocusItem } from "../helpers/fixtures"; function renderWithProviders(ui: React.ReactElement) { const queryClient = new QueryClient({ defaultOptions: { queries: { retry: false, }, }, }); return render( {ui} ); } describe("AppShell", () => { beforeEach(() => { useNavStore.setState({ activeView: "focus" }); useFocusStore.setState({ current: null, queue: [], isLoading: false, error: null, }); useCaptureStore.setState({ isOpen: false, isSubmitting: false, lastCapturedId: null, error: null, }); useInboxStore.setState({ items: [], }); resetMocks(); }); it("renders navigation tabs", () => { renderWithProviders(); expect(screen.getByText("Focus")).toBeInTheDocument(); expect(screen.getByText("Queue")).toBeInTheDocument(); expect(screen.getByText("Inbox")).toBeInTheDocument(); expect(screen.getByText("Debug")).toBeInTheDocument(); }); it("shows Focus view by default", () => { renderWithProviders(); expect(screen.getByText(/all clear/i)).toBeInTheDocument(); }); it("switches to Queue view when Queue tab is clicked", async () => { const user = userEvent.setup(); renderWithProviders(); await user.click(screen.getByText("Queue")); expect(await screen.findByText(/no items/i)).toBeInTheDocument(); }); it("switches to Inbox view and shows inbox zero", async () => { const user = userEvent.setup(); renderWithProviders(); await user.click(screen.getByText("Inbox")); // When inbox is empty, shows "Inbox Zero" / "All caught up!" expect(await screen.findByText(/inbox zero/i)).toBeInTheDocument(); }); it("switches to Debug view when Debug tab is clicked", async () => { const user = userEvent.setup(); renderWithProviders(); await user.click(screen.getByText("Debug")); expect(await screen.findByText(/lore debug/i)).toBeInTheDocument(); }); it("shows queue count badge when items exist", () => { useFocusStore.setState({ current: makeFocusItem({ id: "a" }), queue: [makeFocusItem({ id: "b" }), makeFocusItem({ id: "c" })], }); renderWithProviders(); expect(screen.getByTestId("queue-badge")).toHaveTextContent("3"); }); it("opens quick capture overlay on global shortcut event", async () => { renderWithProviders(); act(() => { // Typed event: GlobalShortcutTriggered has payload { shortcut: string } simulateEvent("global-shortcut-triggered", { shortcut: "quick-capture" }); }); expect(useCaptureStore.getState().isOpen).toBe(true); expect(screen.getByPlaceholderText("Capture a thought...")).toBeInTheDocument(); }); it("clicking queue item sets focus and switches to focus view", async () => { const user = userEvent.setup(); useFocusStore.setState({ current: makeFocusItem({ id: "current", title: "Current" }), queue: [ makeFocusItem({ id: "target", type: "issue", title: "Target item", }), ], }); renderWithProviders(); // Navigate to queue and wait for transition await user.click(screen.getByText("Queue")); const targetItem = await screen.findByText("Target item"); // Click on the target item await user.click(targetItem); // Should switch back to focus view with the target as current expect(useFocusStore.getState().current?.id).toBe("target"); expect(useNavStore.getState().activeView).toBe("focus"); }); it("populates focus store with lore items on mount", async () => { const mockLoreItemsResponse = { items: [ { id: "mr_review:group::repo:200", title: "Review this MR", item_type: "mr_review", project: "group/repo", url: "https://gitlab.com/group/repo/-/merge_requests/200", iid: 200, updated_at: "2026-02-26T10:00:00Z", requested_by: "alice", }, { id: "issue:group::repo:42", title: "Fix the bug", item_type: "issue", project: "group/repo", url: "https://gitlab.com/group/repo/-/issues/42", iid: 42, updated_at: "2026-02-26T09:00:00Z", requested_by: null, }, ], success: true, error: null, }; setMockResponse("get_lore_items", mockLoreItemsResponse); renderWithProviders(); // Wait for the focus store to be populated with lore items await waitFor(() => { const state = useFocusStore.getState(); expect(state.current !== null || state.queue.length > 0).toBe(true); }); // Verify the items were transformed and stored const state = useFocusStore.getState(); const allItems = state.current ? [state.current, ...state.queue] : state.queue; expect(allItems.length).toBe(2); expect(allItems[0].id).toBe("mr_review:group::repo:200"); expect(allItems[0].type).toBe("mr_review"); }); it("clears focus store when lore returns empty items", async () => { // Start with existing items in the store (simulating stale data) useFocusStore.setState({ current: makeFocusItem({ id: "stale-item", title: "Stale Item" }), queue: [makeFocusItem({ id: "stale-queue", title: "Stale Queue Item" })], }); // Lore now returns empty (user cleared their GitLab queue) const mockEmptyResponse = { items: [], success: true, error: null, }; setMockResponse("get_lore_items", mockEmptyResponse); renderWithProviders(); // Wait for the store to be cleared await waitFor(() => { const state = useFocusStore.getState(); expect(state.current).toBeNull(); expect(state.queue.length).toBe(0); }); }); });