Composer.test.tsx (5235B)
1 import { describe, it, expect, vi, beforeEach } from "vitest"; 2 import { render, screen, fireEvent } from "@testing-library/react"; 3 import { Composer } from "./Composer"; 4 5 beforeEach(() => { 6 vi.restoreAllMocks(); 7 }); 8 9 describe("Composer", () => { 10 it("renders textarea with placeholder", () => { 11 render(<Composer onSend={vi.fn()} />); 12 expect(screen.getByPlaceholderText("Type a message...")).toBeInTheDocument(); 13 }); 14 15 it("renders Send button", () => { 16 render(<Composer onSend={vi.fn()} />); 17 expect(screen.getByText("Send")).toBeInTheDocument(); 18 }); 19 20 it("renders attachment label", () => { 21 render(<Composer onSend={vi.fn()} />); 22 expect(screen.getByText("📎")).toBeInTheDocument(); 23 }); 24 25 it("renders help text", () => { 26 render(<Composer onSend={vi.fn()} />); 27 expect(screen.getByText(/Ctrl\+Enter to send/)).toBeInTheDocument(); 28 }); 29 30 it("calls onSend with text when Send is clicked", () => { 31 const onSend = vi.fn(); 32 render(<Composer onSend={onSend} />); 33 const textarea = screen.getByPlaceholderText("Type a message..."); 34 fireEvent.change(textarea, { target: { value: "Hello world" } }); 35 fireEvent.click(screen.getByText("Send")); 36 expect(onSend).toHaveBeenCalledWith("Hello world", undefined); 37 }); 38 39 it("clears text after sending", () => { 40 render(<Composer onSend={vi.fn()} />); 41 const textarea = screen.getByPlaceholderText("Type a message..."); 42 fireEvent.change(textarea, { target: { value: "Hello" } }); 43 fireEvent.click(screen.getByText("Send")); 44 expect(textarea).toHaveValue(""); 45 }); 46 47 it("disables Send button when empty", () => { 48 render(<Composer onSend={vi.fn()} />); 49 expect(screen.getByText("Send")).toBeDisabled(); 50 }); 51 52 it("enables Send button when text is entered", () => { 53 render(<Composer onSend={vi.fn()} />); 54 fireEvent.change(screen.getByPlaceholderText("Type a message..."), { target: { value: "hi" } }); 55 expect(screen.getByText("Send")).not.toBeDisabled(); 56 }); 57 58 it("disables textarea when disabled prop is true", () => { 59 render(<Composer onSend={vi.fn()} disabled />); 60 expect(screen.getByPlaceholderText("Type a message...")).toBeDisabled(); 61 expect(screen.getByText("Send")).toBeDisabled(); 62 }); 63 64 it("calls onSend with Ctrl+Enter", () => { 65 const onSend = vi.fn(); 66 render(<Composer onSend={onSend} />); 67 const textarea = screen.getByPlaceholderText("Type a message..."); 68 fireEvent.change(textarea, { target: { value: "test" } }); 69 fireEvent.keyDown(textarea, { key: "Enter", ctrlKey: true }); 70 expect(onSend).toHaveBeenCalledWith("test", undefined); 71 }); 72 73 it("calls onSend with Meta+Enter", () => { 74 const onSend = vi.fn(); 75 render(<Composer onSend={onSend} />); 76 const textarea = screen.getByPlaceholderText("Type a message..."); 77 fireEvent.change(textarea, { target: { value: "meta" } }); 78 fireEvent.keyDown(textarea, { key: "Enter", metaKey: true }); 79 expect(onSend).toHaveBeenCalledWith("meta", undefined); 80 }); 81 82 it("does not send on Enter without modifier", () => { 83 const onSend = vi.fn(); 84 render(<Composer onSend={onSend} />); 85 const textarea = screen.getByPlaceholderText("Type a message..."); 86 fireEvent.change(textarea, { target: { value: "test" } }); 87 fireEvent.keyDown(textarea, { key: "Enter" }); 88 expect(onSend).not.toHaveBeenCalled(); 89 }); 90 91 it("does not send on Ctrl+Shift+Enter", () => { 92 const onSend = vi.fn(); 93 render(<Composer onSend={onSend} />); 94 const textarea = screen.getByPlaceholderText("Type a message..."); 95 fireEvent.change(textarea, { target: { value: "test" } }); 96 fireEvent.keyDown(textarea, { key: "Enter", ctrlKey: true, shiftKey: true }); 97 expect(onSend).not.toHaveBeenCalled(); 98 }); 99 100 it("does not send whitespace-only text", () => { 101 const onSend = vi.fn(); 102 render(<Composer onSend={onSend} />); 103 const textarea = screen.getByPlaceholderText("Type a message..."); 104 fireEvent.change(textarea, { target: { value: " " } }); 105 fireEvent.click(screen.getByText("Send")); 106 expect(onSend).not.toHaveBeenCalled(); 107 }); 108 109 it("trims whitespace before sending", () => { 110 const onSend = vi.fn(); 111 render(<Composer onSend={onSend} />); 112 const textarea = screen.getByPlaceholderText("Type a message..."); 113 fireEvent.change(textarea, { target: { value: " hello " } }); 114 fireEvent.click(screen.getByText("Send")); 115 expect(onSend).toHaveBeenCalledWith("hello", undefined); 116 }); 117 118 it("does not send when disabled even with text", () => { 119 const onSend = vi.fn(); 120 render(<Composer onSend={onSend} disabled />); 121 const textarea = screen.getByPlaceholderText("Type a message..."); 122 fireEvent.change(textarea, { target: { value: "test" } }); 123 fireEvent.click(screen.getByText("Send")); 124 expect(onSend).not.toHaveBeenCalled(); 125 }); 126 127 it("shows drop zone", () => { 128 render(<Composer onSend={vi.fn()} />); 129 expect(screen.getByTestId("composer-drop-zone")).toBeInTheDocument(); 130 }); 131 132 it("textarea auto-adjusts height on text change", () => { 133 render(<Composer onSend={vi.fn()} />); 134 const textarea = screen.getByPlaceholderText("Type a message..."); 135 expect(textarea).toBeInTheDocument(); 136 }); 137 });