/** * Tests for ReasonPrompt component. * * TDD: These tests define the expected behavior before implementation. */ import { describe, it, expect, vi, beforeEach } from "vitest"; import { render, screen } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import { ReasonPrompt } from "@/components/ReasonPrompt"; describe("ReasonPrompt", () => { const defaultProps = { action: "set_focus", itemTitle: "Review MR !847", onSubmit: vi.fn(), onCancel: vi.fn(), }; beforeEach(() => { vi.clearAllMocks(); }); it("renders with action context", () => { render(); expect( screen.getByText(/Setting focus to.*Review MR !847/i) ).toBeInTheDocument(); }); it("captures text input", async () => { const user = userEvent.setup(); const onSubmit = vi.fn(); render(); await user.type( screen.getByRole("textbox"), "Sarah pinged me, she is blocked" ); await user.click(screen.getByRole("button", { name: /confirm/i })); expect(onSubmit).toHaveBeenCalledWith( expect.objectContaining({ reason: "Sarah pinged me, she is blocked", }) ); }); it("allows selecting quick tags", async () => { const user = userEvent.setup(); const onSubmit = vi.fn(); render(); await user.click(screen.getByRole("button", { name: /blocking/i })); await user.click(screen.getByRole("button", { name: /urgent/i })); await user.click(screen.getByRole("button", { name: /confirm/i })); expect(onSubmit).toHaveBeenCalledWith( expect.objectContaining({ tags: expect.arrayContaining(["blocking", "urgent"]), }) ); }); it("toggles tag off when clicked again", async () => { const user = userEvent.setup(); const onSubmit = vi.fn(); render(); const blockingTag = screen.getByRole("button", { name: /blocking/i }); // Select then deselect await user.click(blockingTag); await user.click(blockingTag); await user.click(screen.getByRole("button", { name: /confirm/i })); expect(onSubmit).toHaveBeenCalledWith( expect.objectContaining({ tags: [], }) ); }); it("allows skipping reason", async () => { const user = userEvent.setup(); const onSubmit = vi.fn(); render(); await user.click(screen.getByRole("button", { name: /skip reason/i })); expect(onSubmit).toHaveBeenCalledWith({ reason: null, tags: [], }); }); it("trims whitespace from reason", async () => { const user = userEvent.setup(); const onSubmit = vi.fn(); render(); await user.type(screen.getByRole("textbox"), " spaced reason "); await user.click(screen.getByRole("button", { name: /confirm/i })); expect(onSubmit).toHaveBeenCalledWith( expect.objectContaining({ reason: "spaced reason", }) ); }); it("treats empty reason as null", async () => { const user = userEvent.setup(); const onSubmit = vi.fn(); render(); // Just click confirm without typing await user.click(screen.getByRole("button", { name: /confirm/i })); expect(onSubmit).toHaveBeenCalledWith( expect.objectContaining({ reason: null, }) ); }); it("cancels on Escape key", async () => { const user = userEvent.setup(); const onCancel = vi.fn(); render(); await user.keyboard("{Escape}"); expect(onCancel).toHaveBeenCalled(); }); it("displays all quick tag options", () => { render(); expect(screen.getByRole("button", { name: /blocking/i })).toBeInTheDocument(); expect(screen.getByRole("button", { name: /urgent/i })).toBeInTheDocument(); expect(screen.getByRole("button", { name: /context switch/i })).toBeInTheDocument(); expect(screen.getByRole("button", { name: /energy/i })).toBeInTheDocument(); }); it("shows different titles for different actions", () => { const { rerender } = render( ); expect(screen.getByText(/Deferring.*Issue #42/i)).toBeInTheDocument(); rerender( ); expect(screen.getByText(/Skipping.*MR !100/i)).toBeInTheDocument(); }); });