/** * @file Workspace.laneCwd.test.tsx * @description The console's working directory must be the selected lane's own * `cwd` — on first paint (the page auto-selects the first lane) and after every * lane switch. A stale cwd is not cosmetic: `RunSetup` submits it verbatim, so * the run would be started in the previously selected lane's folder. * @author Nguyễn Ngọc Trí Vĩ */ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; import { render, act, screen, fireEvent } from "@testing-library/react"; import { MemoryRouter } from "react-router-dom"; import i18n from "i18next"; // Hoisted: `vi.mock`'s factory is lifted above the module body, so the // fixtures it reads have to be lifted with it. const { HOME, LANE_A, LANE_B } = vi.hoisted(() => { const laneStub = (id: number, cwd: string) => ({ id, title: `lane-${id}`, cwd, 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", detected_stage: null, detected_signal: null, slot: null, ports: {}, active_feature_id: null, }); return { HOME: { kind: "home", path: "/Users/tester", label: "Home" }, LANE_A: laneStub(1, "/workspace/alpha"), LANE_B: laneStub(2, "/workspace/beta"), }; }); vi.mock("../../lib/api", async (importOriginal) => { const actual = await importOriginal>(); const r = (value: unknown) => vi.fn().mockResolvedValue(value); return { ...actual, api: { run: { list: r({ items: [] }), history: r({ items: [] }), binary: r({ found: true, path: "/usr/bin/claude" }), cwds: r({ items: [HOME] }), files: r({ items: [] }), start: r({ id: "run-1", status: "running" }), get: r({ id: "run-1", status: "running" }), }, lanes: { list: r({ lanes: [LANE_A, LANE_B], counts: { total: 2, running: 0, needs_you: 0, dead: 0 }, }), pipelines: r({ pipelines: [] }), git: r({ available: false }), runtime: r({ up: false, ports: {}, lastError: null }), features: { list: r({ features: [] }), show: r({ feature: null }) }, proof: { list: r({ features: [] }), imageUrl: () => "" }, }, aiRouters: { list: r({ items: [] }) }, sessions: { list: r({ sessions: [], total: 0, limit: 50, offset: 0 }) }, }, }; }); vi.mock("../../lib/eventBus", () => ({ eventBus: { subscribe: () => () => {}, publish: () => {}, onConnection: () => () => {}, connected: true, setConnected: () => {}, }, })); import { Workspace } from "../Workspace"; class ObserverStub { observe() {} unobserve() {} disconnect() {} takeRecords() { return []; } } globalThis.ResizeObserver = globalThis.ResizeObserver || (ObserverStub as unknown as typeof ResizeObserver); async function settle() { await act(async () => { await new Promise((r) => setTimeout(r, 0)); await new Promise((r) => setTimeout(r, 0)); }); } function cwdInput(): HTMLInputElement { return screen.getByPlaceholderText(i18n.t("run:fields.cwdPlaceholder")) as HTMLInputElement; } beforeEach(() => { i18n.changeLanguage("en"); }); afterEach(() => { vi.clearAllMocks(); }); describe("Workspace — the console cwd follows the selected lane", () => { it("shows the auto-selected first lane's cwd on load, not the home default", async () => { render( ); await settle(); expect(cwdInput().value).toBe(LANE_A.cwd); }); it("swaps the cwd when another lane is selected in the strip", async () => { render( ); await settle(); fireEvent.click(screen.getByTestId("lane-tile-2")); await settle(); expect(cwdInput().value).toBe(LANE_B.cwd); fireEvent.click(screen.getByTestId("lane-tile-1")); await settle(); expect(cwdInput().value).toBe(LANE_A.cwd); }); });