import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; import { render, screen } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import { TrayPopover } from "@/components/TrayPopover"; import { makeFocusItem } from "../helpers/fixtures"; describe("TrayPopover", () => { describe("with focus item", () => { it("shows 'THE ONE THING' header", () => { const item = makeFocusItem({ title: "Review MR !847" }); render(); expect(screen.getByText("THE ONE THING")).toBeInTheDocument(); }); it("shows focus item title", () => { const item = makeFocusItem({ title: "Fix auth bug" }); render(); expect(screen.getByText("Fix auth bug")).toBeInTheDocument(); }); it("shows project path", () => { const item = makeFocusItem({ project: "platform/core" }); render(); expect(screen.getByText(/platform\/core/)).toBeInTheDocument(); }); it("shows type badge", () => { const item = makeFocusItem({ type: "mr_review" }); render(); expect(screen.getByText(/review/i)).toBeInTheDocument(); }); it("shows Start button", () => { const item = makeFocusItem(); render(); expect(screen.getByRole("button", { name: /start/i })).toBeInTheDocument(); }); it("shows Defer button", () => { const item = makeFocusItem(); render(); expect(screen.getByRole("button", { name: /defer/i })).toBeInTheDocument(); }); it("shows Skip button", () => { const item = makeFocusItem(); render(); expect(screen.getByRole("button", { name: /skip/i })).toBeInTheDocument(); }); }); describe("empty state", () => { it("shows empty message when no focus item", () => { render(); expect(screen.getByText(/nothing focused/i)).toBeInTheDocument(); }); it("does not show action buttons when empty", () => { render(); expect(screen.queryByRole("button", { name: /start/i })).not.toBeInTheDocument(); expect(screen.queryByRole("button", { name: /defer/i })).not.toBeInTheDocument(); expect(screen.queryByRole("button", { name: /skip/i })).not.toBeInTheDocument(); }); }); describe("counts", () => { it("shows queue count", () => { render(); expect(screen.getByText("Queue: 4")).toBeInTheDocument(); }); it("shows inbox count", () => { render(); expect(screen.getByText("Inbox: 3")).toBeInTheDocument(); }); it("shows both counts", () => { render(); expect(screen.getByText("Queue: 5")).toBeInTheDocument(); expect(screen.getByText("Inbox: 2")).toBeInTheDocument(); }); }); describe("full window link", () => { it("shows keyboard shortcut hint", () => { render(); // Should show some reference to opening full window expect(screen.getByText(/full window/i)).toBeInTheDocument(); }); }); describe("actions", () => { it("calls onStart when Start clicked", async () => { const user = userEvent.setup(); const onStart = vi.fn(); const item = makeFocusItem(); render( ); await user.click(screen.getByRole("button", { name: /start/i })); expect(onStart).toHaveBeenCalledTimes(1); }); it("calls onDefer with duration when Defer clicked", async () => { const user = userEvent.setup(); const onDefer = vi.fn(); const item = makeFocusItem(); render( ); await user.click(screen.getByRole("button", { name: /defer/i })); expect(onDefer).toHaveBeenCalledWith("1h"); }); it("calls onSkip when Skip clicked", async () => { const user = userEvent.setup(); const onSkip = vi.fn(); const item = makeFocusItem(); render( ); await user.click(screen.getByRole("button", { name: /skip/i })); expect(onSkip).toHaveBeenCalledTimes(1); }); it("calls onOpenFull when full window link clicked", async () => { const user = userEvent.setup(); const onOpenFull = vi.fn(); render( ); await user.click(screen.getByText(/full window/i)); expect(onOpenFull).toHaveBeenCalledTimes(1); }); }); describe("relative time", () => { beforeEach(() => { vi.useFakeTimers(); vi.setSystemTime(new Date("2026-02-26T12:00:00Z")); }); afterEach(() => { vi.useRealTimers(); }); it("shows age for items", () => { const item = makeFocusItem({ updatedAt: new Date("2026-02-24T12:00:00Z").toISOString(), // 2 days ago }); render(); expect(screen.getByText(/2d/)).toBeInTheDocument(); }); }); });