/** * @file LaneConsolePane.test.tsx * @description Test suite for the LaneConsolePane component * @author Nguyễn Ngọc Trí Vĩ */ import { describe, it, expect, vi, beforeEach } from "vitest"; import { render, screen, fireEvent, waitFor } from "@testing-library/react"; import { LaneConsolePane } from "../LaneConsolePane"; import { api } from "../../../lib/api"; import type { Lane } from "../../../lib/types"; vi.mock("../TerminalView", () => ({ TerminalView: ({ runId }: { runId: string }) => (
), })); vi.mock("../../../lib/api", () => ({ api: { lanes: { ensure: vi.fn(), action: vi.fn(), list: vi.fn(), }, run: { list: vi.fn().mockResolvedValue({ items: [] }), history: vi.fn().mockResolvedValue({ items: [] }), get: vi.fn(), start: vi.fn(), }, }, RUN_MODEL_CHOICES: [], RUN_EFFORT_CHOICES: [], })); const LANE: Lane = { id: 1, title: "demo", cwd: "/workspace/a", branch: null, kind: "adopted", source_repo: null, pipeline: "default", session_id: null, run_id: null, stage: "idle", stage_since: null, status: "idle", gate_decision: null, ci_status: null, needs_action: null, links: {}, stages: {}, notes: null, pipeline_name: "Default", pipeline_nodes: [], progress: 0, stage_seconds: null, last_event_seconds: null, liveness: "idle" as Lane["liveness"], detected_stage: null, detected_signal: null, slot: null, ports: {}, active_feature_id: null, }; function baseProps() { return { lanes: [LANE], laneId: 1, showLaneSelector: false, onLaneIdChange: vi.fn(), onLaneCreated: vi.fn(), binaryStatus: { found: true, path: "/usr/local/bin/claude" }, cwdSuggestions: [], activeRuns: { items: [] }, wsConnected: true, }; } describe("LaneConsolePane", () => { beforeEach(() => { vi.clearAllMocks(); }); it("starts a run through /api/lanes//start, not /api/run/start", async () => { (api.lanes.action as ReturnType).mockResolvedValue({ lane: { ...LANE, run_id: "run-1" }, }); (api.run.get as ReturnType).mockResolvedValue({ id: "run-1", laneId: 1, status: "running", cwd: "/workspace/a", model: null, permissionMode: null, effort: null, resumeSessionId: null, sessionId: null, startedAt: null, promptPreview: null, }); render(); // Set cwd and prompt const cwdInput = screen.getByPlaceholderText(/type to search/i); fireEvent.change(cwdInput, { target: { value: "/workspace/a" } }); const promptTextarea = screen.getByPlaceholderText(/ask claude/i); fireEvent.change(promptTextarea, { target: { value: "test prompt" } }); // Find and click the Run button (the main start button in RunSetup) fireEvent.click(screen.getByRole("button", { name: /^run$/i })); await waitFor(() => expect(api.lanes.action).toHaveBeenCalledWith(1, "start", expect.any(Object)) ); expect(api.run.start).not.toHaveBeenCalled(); await waitFor(() => expect(screen.getByTestId("terminal-view")).toHaveAttribute("data-run-id", "run-1") ); }); it("shows a lane dropdown only when showLaneSelector is true", () => { const { rerender } = render(); expect(screen.getByTestId("pane-lane-select")).toBeInTheDocument(); rerender(); expect(screen.queryByTestId("pane-lane-select")).not.toBeInTheDocument(); }); it("renders an empty placeholder when laneId is null and showLaneSelector is false", () => { render(); expect(screen.getByTestId("pane-empty")).toBeInTheDocument(); expect(screen.queryByTestId("console-body")).not.toBeInTheDocument(); }); it("renders the full console with selector when laneId is null but showLaneSelector is true", () => { render(); expect(screen.getByTestId("console-body")).toBeInTheDocument(); expect(screen.getByTestId("pane-lane-select")).toBeInTheDocument(); expect(screen.queryByTestId("pane-empty")).not.toBeInTheDocument(); }); });