All checks were successful
ci/woodpecker/push/web Pipeline was successful
Co-authored-by: Jason Woltje <jason@diversecanvas.com> Co-committed-by: Jason Woltje <jason@diversecanvas.com>
311 lines
8.6 KiB
TypeScript
311 lines
8.6 KiB
TypeScript
/**
|
|
* @file ChatOverlay.test.tsx
|
|
* @description Tests for the ChatOverlay component
|
|
*/
|
|
|
|
import { render, screen, fireEvent } from "@testing-library/react";
|
|
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
import { ChatOverlay } from "./ChatOverlay";
|
|
|
|
// Mock the Chat component
|
|
vi.mock("./Chat", () => ({
|
|
Chat: vi.fn(() => <div data-testid="chat-component">Chat Component</div>),
|
|
}));
|
|
|
|
// Mock the useChatOverlay hook
|
|
const mockOpen = vi.fn();
|
|
const mockClose = vi.fn();
|
|
const mockMinimize = vi.fn();
|
|
const mockExpand = vi.fn();
|
|
const mockToggle = vi.fn();
|
|
const mockToggleMinimize = vi.fn();
|
|
|
|
vi.mock("../../hooks/useChatOverlay", () => ({
|
|
useChatOverlay: vi.fn(() => ({
|
|
isOpen: false,
|
|
isMinimized: false,
|
|
open: mockOpen,
|
|
close: mockClose,
|
|
minimize: mockMinimize,
|
|
expand: mockExpand,
|
|
toggle: mockToggle,
|
|
toggleMinimize: mockToggleMinimize,
|
|
})),
|
|
}));
|
|
|
|
describe("ChatOverlay", () => {
|
|
beforeEach(async () => {
|
|
vi.clearAllMocks();
|
|
// Reset mock to default state
|
|
const { useChatOverlay } = await import("../../hooks/useChatOverlay");
|
|
vi.mocked(useChatOverlay).mockReturnValue({
|
|
isOpen: false,
|
|
isMinimized: false,
|
|
open: mockOpen,
|
|
close: mockClose,
|
|
minimize: mockMinimize,
|
|
expand: mockExpand,
|
|
toggle: mockToggle,
|
|
toggleMinimize: mockToggleMinimize,
|
|
});
|
|
});
|
|
|
|
describe("when closed", () => {
|
|
it("should render a floating button to open the chat", () => {
|
|
render(<ChatOverlay />);
|
|
|
|
const openButton = screen.getByRole("button", { name: /open chat/i });
|
|
expect(openButton).toBeDefined();
|
|
});
|
|
|
|
it("should not render the chat component when closed", () => {
|
|
render(<ChatOverlay />);
|
|
|
|
const chatComponent = screen.queryByTestId("chat-component");
|
|
expect(chatComponent).toBeNull();
|
|
});
|
|
|
|
it("should call open when the floating button is clicked", () => {
|
|
render(<ChatOverlay />);
|
|
|
|
const openButton = screen.getByRole("button", { name: /open chat/i });
|
|
fireEvent.click(openButton);
|
|
|
|
expect(mockOpen).toHaveBeenCalledOnce();
|
|
});
|
|
});
|
|
|
|
describe("when open", () => {
|
|
beforeEach(async () => {
|
|
const { useChatOverlay } = await import("../../hooks/useChatOverlay");
|
|
vi.mocked(useChatOverlay).mockReturnValue({
|
|
isOpen: true,
|
|
isMinimized: false,
|
|
open: mockOpen,
|
|
close: mockClose,
|
|
minimize: mockMinimize,
|
|
expand: mockExpand,
|
|
toggle: mockToggle,
|
|
toggleMinimize: mockToggleMinimize,
|
|
});
|
|
});
|
|
|
|
it("should render the chat component", () => {
|
|
render(<ChatOverlay />);
|
|
|
|
const chatComponent = screen.getByTestId("chat-component");
|
|
expect(chatComponent).toBeDefined();
|
|
});
|
|
|
|
it("should render a close button", () => {
|
|
render(<ChatOverlay />);
|
|
|
|
const closeButton = screen.getByRole("button", { name: /close chat/i });
|
|
expect(closeButton).toBeDefined();
|
|
});
|
|
|
|
it("should render a minimize button", () => {
|
|
render(<ChatOverlay />);
|
|
|
|
const minimizeButton = screen.getByRole("button", { name: /minimize chat/i });
|
|
expect(minimizeButton).toBeDefined();
|
|
});
|
|
|
|
it("should call close when the close button is clicked", () => {
|
|
render(<ChatOverlay />);
|
|
|
|
const closeButton = screen.getByRole("button", { name: /close chat/i });
|
|
fireEvent.click(closeButton);
|
|
|
|
expect(mockClose).toHaveBeenCalledOnce();
|
|
});
|
|
|
|
it("should call minimize when the minimize button is clicked", () => {
|
|
render(<ChatOverlay />);
|
|
|
|
const minimizeButton = screen.getByRole("button", { name: /minimize chat/i });
|
|
fireEvent.click(minimizeButton);
|
|
|
|
expect(mockMinimize).toHaveBeenCalledOnce();
|
|
});
|
|
});
|
|
|
|
describe("when minimized", () => {
|
|
beforeEach(async () => {
|
|
const { useChatOverlay } = await import("../../hooks/useChatOverlay");
|
|
vi.mocked(useChatOverlay).mockReturnValue({
|
|
isOpen: true,
|
|
isMinimized: true,
|
|
open: mockOpen,
|
|
close: mockClose,
|
|
minimize: mockMinimize,
|
|
expand: mockExpand,
|
|
toggle: mockToggle,
|
|
toggleMinimize: mockToggleMinimize,
|
|
});
|
|
});
|
|
|
|
it("should not render the chat component when minimized", () => {
|
|
render(<ChatOverlay />);
|
|
|
|
const chatComponent = screen.queryByTestId("chat-component");
|
|
expect(chatComponent).toBeNull();
|
|
});
|
|
|
|
it("should render a minimized header", () => {
|
|
render(<ChatOverlay />);
|
|
|
|
const header = screen.getByText(/jarvis/i);
|
|
expect(header).toBeDefined();
|
|
});
|
|
|
|
it("should call expand when clicking the minimized header", () => {
|
|
render(<ChatOverlay />);
|
|
|
|
const header = screen.getByText(/jarvis/i);
|
|
fireEvent.click(header);
|
|
|
|
expect(mockExpand).toHaveBeenCalledOnce();
|
|
});
|
|
});
|
|
|
|
describe("keyboard shortcuts", () => {
|
|
it("should toggle chat when Cmd+Shift+J is pressed", () => {
|
|
render(<ChatOverlay />);
|
|
|
|
fireEvent.keyDown(document, {
|
|
key: "j",
|
|
code: "KeyJ",
|
|
metaKey: true,
|
|
shiftKey: true,
|
|
});
|
|
|
|
expect(mockToggle).toHaveBeenCalledOnce();
|
|
});
|
|
|
|
it("should toggle chat when Ctrl+Shift+J is pressed", () => {
|
|
render(<ChatOverlay />);
|
|
|
|
fireEvent.keyDown(document, {
|
|
key: "j",
|
|
code: "KeyJ",
|
|
ctrlKey: true,
|
|
shiftKey: true,
|
|
});
|
|
|
|
expect(mockToggle).toHaveBeenCalledOnce();
|
|
});
|
|
|
|
it("should minimize chat when Escape is pressed and chat is open", async () => {
|
|
const { useChatOverlay } = await import("../../hooks/useChatOverlay");
|
|
vi.mocked(useChatOverlay).mockReturnValue({
|
|
isOpen: true,
|
|
isMinimized: false,
|
|
open: mockOpen,
|
|
close: mockClose,
|
|
minimize: mockMinimize,
|
|
expand: mockExpand,
|
|
toggle: mockToggle,
|
|
toggleMinimize: mockToggleMinimize,
|
|
});
|
|
|
|
render(<ChatOverlay />);
|
|
|
|
fireEvent.keyDown(document, {
|
|
key: "Escape",
|
|
code: "Escape",
|
|
});
|
|
|
|
expect(mockMinimize).toHaveBeenCalledOnce();
|
|
});
|
|
|
|
it("should open chat when Cmd+K is pressed and chat is closed", async () => {
|
|
render(<ChatOverlay />);
|
|
|
|
// Wait for component to mount
|
|
await screen.findByRole("button", { name: /open chat/i });
|
|
|
|
fireEvent.keyDown(document, {
|
|
key: "k",
|
|
code: "KeyK",
|
|
metaKey: true,
|
|
});
|
|
|
|
expect(mockOpen).toHaveBeenCalled();
|
|
});
|
|
|
|
it("should open chat when Ctrl+K is pressed and chat is closed", async () => {
|
|
render(<ChatOverlay />);
|
|
|
|
// Wait for component to mount
|
|
await screen.findByRole("button", { name: /open chat/i });
|
|
|
|
fireEvent.keyDown(document, {
|
|
key: "k",
|
|
code: "KeyK",
|
|
ctrlKey: true,
|
|
});
|
|
|
|
expect(mockOpen).toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
describe("new conversation button", () => {
|
|
it("should render the new conversation button when chat is open", async () => {
|
|
const { useChatOverlay } = await import("../../hooks/useChatOverlay");
|
|
vi.mocked(useChatOverlay).mockReturnValue({
|
|
isOpen: true,
|
|
isMinimized: false,
|
|
open: mockOpen,
|
|
close: mockClose,
|
|
minimize: mockMinimize,
|
|
expand: mockExpand,
|
|
toggle: mockToggle,
|
|
toggleMinimize: mockToggleMinimize,
|
|
});
|
|
|
|
render(<ChatOverlay />);
|
|
|
|
const newConvBtn = screen.getByRole("button", { name: /new conversation/i });
|
|
expect(newConvBtn).toBeDefined();
|
|
});
|
|
|
|
it("should have a tooltip on the new conversation button", async () => {
|
|
const { useChatOverlay } = await import("../../hooks/useChatOverlay");
|
|
vi.mocked(useChatOverlay).mockReturnValue({
|
|
isOpen: true,
|
|
isMinimized: false,
|
|
open: mockOpen,
|
|
close: mockClose,
|
|
minimize: mockMinimize,
|
|
expand: mockExpand,
|
|
toggle: mockToggle,
|
|
toggleMinimize: mockToggleMinimize,
|
|
});
|
|
|
|
render(<ChatOverlay />);
|
|
|
|
const newConvBtn = screen.getByRole("button", { name: /new conversation/i });
|
|
expect(newConvBtn.getAttribute("title")).toContain("Cmd+N");
|
|
});
|
|
});
|
|
|
|
describe("responsive design", () => {
|
|
it("should render as a sidebar on desktop", () => {
|
|
render(<ChatOverlay />);
|
|
|
|
// Check for desktop-specific classes (will be verified in implementation)
|
|
// This is a placeholder - actual implementation will have proper responsive classes
|
|
expect(true).toBe(true);
|
|
});
|
|
|
|
it("should render as a drawer on mobile", () => {
|
|
render(<ChatOverlay />);
|
|
|
|
// Check for mobile-specific classes (will be verified in implementation)
|
|
// This is a placeholder - actual implementation will have proper responsive classes
|
|
expect(true).toBe(true);
|
|
});
|
|
});
|
|
});
|