ea84b913b4
* feat: choose worktree when opening IDE on multi-repo tasks On multi-repo tasks the open-in-IDE button always opened the first worktree. The editors menu now shows a worktree picker (repo + branch) when a session has multiple worktrees, and each editor in the dropdown expands into a per-worktree submenu. - open-editor/open-folder accept an optional worktree_id; empty keeps the first-worktree fallback so existing clients are unaffected - session DTOs now serialize the full worktrees list instead of only the flattened first-worktree fields - useSessionWorktrees unions API worktrees with the WS-populated list - new Playwright spec covers the picker on a real multi-repo session * fix: address review feedback on worktree picker - fall back on empty-string worktree fields, not only null/undefined - bind optional open-folder body via EOF check instead of ContentLength so chunked requests keep their worktree_id - wrap disabled editor buttons in a focusable span so tooltips work - add hook test asserting worktreeId is forwarded as worktree_id * fix: map editor service errors to HTTP status, drop e2e sleep - open-editor/open-folder return 404/400/409 for known service errors (unknown worktree or editor, invalid config, unavailable editor) instead of a blanket 500 - replace the picker spec's 750ms sleep with an explicit composer click so the autofocus race is deterministic --------- Co-authored-by: Kandev Agent <agent@kandev.dev>
67 lines
2.0 KiB
TypeScript
67 lines
2.0 KiB
TypeScript
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
import { renderHook } from "@testing-library/react";
|
|
|
|
const mockOpenSessionInEditor = vi.fn();
|
|
const mockOpenFileInVscode = vi.fn();
|
|
const mockOpenInternalVscode = vi.fn();
|
|
|
|
vi.mock("@/lib/api", () => ({
|
|
openSessionInEditor: (...args: unknown[]) => mockOpenSessionInEditor(...args),
|
|
}));
|
|
|
|
vi.mock("@/lib/api/domains/vscode-api", () => ({
|
|
openFileInVscode: (...args: unknown[]) => mockOpenFileInVscode(...args),
|
|
}));
|
|
|
|
vi.mock("@/lib/state/dockview-store", () => ({
|
|
useDockviewStore: {
|
|
getState: () => ({ openInternalVscode: mockOpenInternalVscode }),
|
|
},
|
|
}));
|
|
|
|
vi.mock("@/lib/http/use-request", () => ({
|
|
useRequest: (fn: (options?: unknown) => Promise<unknown>) => ({
|
|
run: fn,
|
|
status: "idle",
|
|
isLoading: false,
|
|
}),
|
|
}));
|
|
|
|
import { useOpenSessionInEditor } from "./use-open-session-in-editor";
|
|
|
|
describe("useOpenSessionInEditor", () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
mockOpenSessionInEditor.mockResolvedValue({});
|
|
});
|
|
|
|
it("forwards the selected worktree as worktree_id", async () => {
|
|
const { result } = renderHook(() => useOpenSessionInEditor("session-1"));
|
|
|
|
await result.current.open({ editorId: "editor-1", worktreeId: "wt-2" });
|
|
|
|
expect(mockOpenSessionInEditor).toHaveBeenCalledWith(
|
|
"session-1",
|
|
expect.objectContaining({ editor_id: "editor-1", worktree_id: "wt-2" }),
|
|
expect.anything(),
|
|
);
|
|
});
|
|
|
|
it("omits worktree_id when no worktree is selected", async () => {
|
|
const { result } = renderHook(() => useOpenSessionInEditor("session-1"));
|
|
|
|
await result.current.open({ editorId: "editor-1" });
|
|
|
|
const payload = mockOpenSessionInEditor.mock.calls[0][1] as Record<string, unknown>;
|
|
expect(payload.worktree_id).toBeUndefined();
|
|
});
|
|
|
|
it("does not call the API without a session id", async () => {
|
|
const { result } = renderHook(() => useOpenSessionInEditor(null));
|
|
|
|
await result.current.open({ editorId: "editor-1", worktreeId: "wt-2" });
|
|
|
|
expect(mockOpenSessionInEditor).not.toHaveBeenCalled();
|
|
});
|
|
});
|