theo-agent-dashboard

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

SessionList.test.tsx (4233B)


      1 import { describe, it, expect, vi, beforeEach } from "vitest";
      2 import { render, screen, fireEvent, waitFor } from "@testing-library/react";
      3 import { SessionList } from "./SessionList";
      4 import { useSessionStore } from "../../store/session";
      5 
      6 const sessions = [
      7   { id: "s1", name: "Chat One", created_at: 1000, message_count: 1 },
      8   { id: "s2", name: "Chat Two", created_at: 2000, message_count: 1 },
      9 ];
     10 
     11 beforeEach(() => {
     12   useSessionStore.setState({
     13     sessions: [],
     14     activeSessionId: null,
     15     loading: false,
     16     error: null,
     17     _allSessions: [],
     18   });
     19   vi.restoreAllMocks();
     20 });
     21 
     22 const mockFetchReturn = (data: unknown) =>
     23   vi.fn().mockResolvedValue({ ok: true, json: async () => data });
     24 
     25 describe("SessionList", () => {
     26   it("shows 'No sessions yet' when empty", () => {
     27     vi.stubGlobal("fetch", mockFetchReturn({ sessions: [] }));
     28     render(<SessionList />);
     29     expect(screen.getByText("No sessions yet")).toBeInTheDocument();
     30   });
     31 
     32   it("renders session names", async () => {
     33     vi.stubGlobal("fetch", mockFetchReturn({ sessions }));
     34     render(<SessionList />);
     35     await waitFor(() => {
     36       expect(screen.getByText("Chat One")).toBeInTheDocument();
     37       expect(screen.getByText("Chat Two")).toBeInTheDocument();
     38     });
     39   });
     40 
     41   it("highlights active session", () => {
     42     useSessionStore.setState({
     43       sessions: [sessions[0]],
     44       _allSessions: [sessions[0]],
     45       activeSessionId: "s1",
     46     });
     47     vi.stubGlobal("fetch", mockFetchReturn({ sessions: [sessions[0]] }));
     48     const { container } = render(<SessionList />);
     49     const activeItem = container.querySelector(".bg-indigo-600\\/20");
     50     expect(activeItem).toBeTruthy();
     51   });
     52 
     53   it("shows creation date in metadata", async () => {
     54     vi.stubGlobal("fetch", mockFetchReturn({ sessions: [{ id: "s1", name: "Chat", created_at: 1700000000, message_count: 1 }] }));
     55     render(<SessionList />);
     56     await waitFor(() => {
     57       expect(screen.getByText(/2023/)).toBeInTheDocument();
     58     });
     59   });
     60 
     61   it("opens context menu on ⋮ click", async () => {
     62     vi.stubGlobal("fetch", mockFetchReturn({ sessions: [sessions[0]] }));
     63     render(<SessionList />);
     64     await waitFor(() => expect(screen.getByText("Chat One")).toBeInTheDocument());
     65     fireEvent.click(screen.getByText("⋮"));
     66     await waitFor(() => {
     67       expect(screen.getByText("Rename")).toBeInTheDocument();
     68       expect(screen.getByText("Delete")).toBeInTheDocument();
     69     });
     70   });
     71 
     72   it("entering rename mode shows input", async () => {
     73     vi.stubGlobal("fetch", mockFetchReturn({ sessions: [{ id: "s1", name: "Old Name", message_count: 1 }] }));
     74     render(<SessionList />);
     75     await waitFor(() => expect(screen.getByText("Old Name")).toBeInTheDocument());
     76     fireEvent.click(screen.getByText("⋮"));
     77     await waitFor(() => expect(screen.getByText("Rename")).toBeInTheDocument());
     78     fireEvent.click(screen.getByText("Rename"));
     79     await waitFor(() => {
     80       expect(screen.getByDisplayValue("Old Name")).toBeInTheDocument();
     81     });
     82   });
     83 
     84   it("delete confirmation shows cancel button", async () => {
     85     vi.stubGlobal("fetch", mockFetchReturn({ sessions: [{ id: "s1", name: "To Delete", message_count: 1 }] }));
     86     render(<SessionList />);
     87     await waitFor(() => expect(screen.getByText("To Delete")).toBeInTheDocument());
     88     fireEvent.click(screen.getByText("⋮"));
     89     await waitFor(() => expect(screen.getByText("Delete")).toBeInTheDocument());
     90     const deleteBtns = screen.getAllByText("Delete");
     91     fireEvent.click(deleteBtns[0]);
     92     await waitFor(() => {
     93       expect(screen.getByText("Cancel")).toBeInTheDocument();
     94     });
     95   });
     96 
     97   it("new session button clears active session instead of creating one", async () => {
     98     useSessionStore.setState({
     99       sessions: [sessions[0]],
    100       _allSessions: [sessions[0]],
    101       activeSessionId: "s1",
    102     });
    103     vi.stubGlobal("fetch", mockFetchReturn({ sessions: [sessions[0]] }));
    104     render(<SessionList />);
    105     fireEvent.click(screen.getByTitle("New session"));
    106     expect(useSessionStore.getState().activeSessionId).toBeNull();
    107     // Should NOT call POST /api/sessions
    108     expect(fetch).not.toHaveBeenCalledWith(
    109       "/api/sessions",
    110       expect.objectContaining({ method: "POST" }),
    111     );
    112   });
    113 });