chat-input.test.tsx 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. import userEvent from "@testing-library/user-event";
  2. import { fireEvent, render, screen } from "@testing-library/react";
  3. import { describe, afterEach, vi, it, expect } from "vitest";
  4. import { ChatInput } from "#/components/chat-input";
  5. describe("ChatInput", () => {
  6. const onSubmitMock = vi.fn();
  7. afterEach(() => {
  8. vi.clearAllMocks();
  9. });
  10. it("should render a textarea", () => {
  11. render(<ChatInput onSubmit={onSubmitMock} />);
  12. expect(screen.getByTestId("chat-input")).toBeInTheDocument();
  13. expect(screen.getByRole("textbox")).toBeInTheDocument();
  14. });
  15. it("should call onSubmit when the user types and presses enter", async () => {
  16. const user = userEvent.setup();
  17. render(<ChatInput onSubmit={onSubmitMock} />);
  18. const textarea = screen.getByRole("textbox");
  19. await user.type(textarea, "Hello, world!");
  20. await user.keyboard("{Enter}");
  21. expect(onSubmitMock).toHaveBeenCalledWith("Hello, world!");
  22. });
  23. it("should call onSubmit when pressing the submit button", async () => {
  24. const user = userEvent.setup();
  25. render(<ChatInput onSubmit={onSubmitMock} />);
  26. const textarea = screen.getByRole("textbox");
  27. const button = screen.getByRole("button");
  28. await user.type(textarea, "Hello, world!");
  29. await user.click(button);
  30. expect(onSubmitMock).toHaveBeenCalledWith("Hello, world!");
  31. });
  32. it("should not call onSubmit when the message is empty", async () => {
  33. const user = userEvent.setup();
  34. render(<ChatInput onSubmit={onSubmitMock} />);
  35. const button = screen.getByRole("button");
  36. await user.click(button);
  37. expect(onSubmitMock).not.toHaveBeenCalled();
  38. await user.keyboard("{Enter}");
  39. expect(onSubmitMock).not.toHaveBeenCalled();
  40. });
  41. it("should disable submit", async () => {
  42. const user = userEvent.setup();
  43. render(<ChatInput disabled onSubmit={onSubmitMock} />);
  44. const button = screen.getByRole("button");
  45. const textarea = screen.getByRole("textbox");
  46. await user.type(textarea, "Hello, world!");
  47. expect(button).toBeDisabled();
  48. await user.click(button);
  49. expect(onSubmitMock).not.toHaveBeenCalled();
  50. await user.keyboard("{Enter}");
  51. expect(onSubmitMock).not.toHaveBeenCalled();
  52. });
  53. it("should render a placeholder", () => {
  54. render(
  55. <ChatInput placeholder="Enter your message" onSubmit={onSubmitMock} />,
  56. );
  57. const textarea = screen.getByPlaceholderText("Enter your message");
  58. expect(textarea).toBeInTheDocument();
  59. });
  60. it("should create a newline instead of submitting when shift + enter is pressed", async () => {
  61. const user = userEvent.setup();
  62. render(<ChatInput onSubmit={onSubmitMock} />);
  63. const textarea = screen.getByRole("textbox");
  64. await user.type(textarea, "Hello, world!");
  65. await user.keyboard("{Shift>} {Enter}"); // Shift + Enter
  66. expect(onSubmitMock).not.toHaveBeenCalled();
  67. // expect(textarea).toHaveValue("Hello, world!\n");
  68. });
  69. it("should clear the input message after sending a message", async () => {
  70. const user = userEvent.setup();
  71. render(<ChatInput onSubmit={onSubmitMock} />);
  72. const textarea = screen.getByRole("textbox");
  73. const button = screen.getByRole("button");
  74. await user.type(textarea, "Hello, world!");
  75. await user.keyboard("{Enter}");
  76. expect(textarea).toHaveValue("");
  77. await user.type(textarea, "Hello, world!");
  78. await user.click(button);
  79. expect(textarea).toHaveValue("");
  80. });
  81. it("should hide the submit button", () => {
  82. render(<ChatInput onSubmit={onSubmitMock} showButton={false} />);
  83. expect(screen.queryByRole("button")).not.toBeInTheDocument();
  84. });
  85. it("should call onChange when the user types", async () => {
  86. const user = userEvent.setup();
  87. const onChangeMock = vi.fn();
  88. render(<ChatInput onSubmit={onSubmitMock} onChange={onChangeMock} />);
  89. const textarea = screen.getByRole("textbox");
  90. await user.type(textarea, "Hello, world!");
  91. expect(onChangeMock).toHaveBeenCalledTimes("Hello, world!".length);
  92. });
  93. it("should have set the passed value", () => {
  94. render(<ChatInput value="Hello, world!" onSubmit={onSubmitMock} />);
  95. const textarea = screen.getByRole("textbox");
  96. expect(textarea).toHaveValue("Hello, world!");
  97. });
  98. it("should display the stop button and trigger the callback", async () => {
  99. const user = userEvent.setup();
  100. const onStopMock = vi.fn();
  101. render(
  102. <ChatInput onSubmit={onSubmitMock} button="stop" onStop={onStopMock} />,
  103. );
  104. const stopButton = screen.getByTestId("stop-button");
  105. await user.click(stopButton);
  106. expect(onStopMock).toHaveBeenCalledOnce();
  107. });
  108. it("should call onFocus and onBlur when the textarea is focused and blurred", async () => {
  109. const user = userEvent.setup();
  110. const onFocusMock = vi.fn();
  111. const onBlurMock = vi.fn();
  112. render(
  113. <ChatInput
  114. onSubmit={onSubmitMock}
  115. onFocus={onFocusMock}
  116. onBlur={onBlurMock}
  117. />,
  118. );
  119. const textarea = screen.getByRole("textbox");
  120. await user.click(textarea);
  121. expect(onFocusMock).toHaveBeenCalledOnce();
  122. await user.tab();
  123. expect(onBlurMock).toHaveBeenCalledOnce();
  124. });
  125. it("should handle text paste correctly", () => {
  126. const onSubmit = vi.fn();
  127. const onChange = vi.fn();
  128. render(<ChatInput onSubmit={onSubmit} onChange={onChange} />);
  129. const input = screen.getByTestId("chat-input").querySelector("textarea");
  130. expect(input).toBeTruthy();
  131. // Fire paste event with text data
  132. fireEvent.paste(input!, {
  133. clipboardData: {
  134. getData: (type: string) => type === 'text/plain' ? 'test paste' : '',
  135. files: []
  136. }
  137. });
  138. });
  139. it("should handle image paste correctly", () => {
  140. const onSubmit = vi.fn();
  141. const onImagePaste = vi.fn();
  142. render(<ChatInput onSubmit={onSubmit} onImagePaste={onImagePaste} />);
  143. const input = screen.getByTestId("chat-input").querySelector("textarea");
  144. expect(input).toBeTruthy();
  145. // Create a paste event with an image file
  146. const file = new File(["dummy content"], "image.png", { type: "image/png" });
  147. // Fire paste event with image data
  148. fireEvent.paste(input!, {
  149. clipboardData: {
  150. getData: () => '',
  151. files: [file]
  152. }
  153. });
  154. // Verify image paste was handled
  155. expect(onImagePaste).toHaveBeenCalledWith([file]);
  156. });
  157. });