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>
45 lines
1.8 KiB
TypeScript
45 lines
1.8 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import { buildWorktreeOptions } from "./editor-worktree-options";
|
|
import type { Worktree } from "@/lib/state/slices/session/types";
|
|
import type { Repository } from "@/lib/types/http";
|
|
|
|
function repo(id: string, name: string): Repository {
|
|
return { id, name } as Repository;
|
|
}
|
|
|
|
describe("buildWorktreeOptions", () => {
|
|
it("labels worktrees with their repository name", () => {
|
|
const worktrees: Worktree[] = [
|
|
{ id: "wt-1", sessionId: "s-1", repositoryId: "repo-a", path: "/tasks/x/a", branch: "kd/x" },
|
|
{ id: "wt-2", sessionId: "s-1", repositoryId: "repo-b", path: "/tasks/x/b", branch: "kd/y" },
|
|
];
|
|
const options = buildWorktreeOptions(worktrees, [repo("repo-a", "api"), repo("repo-b", "web")]);
|
|
expect(options).toEqual([
|
|
{ worktreeId: "wt-1", label: "api", branch: "kd/x" },
|
|
{ worktreeId: "wt-2", label: "web", branch: "kd/y" },
|
|
]);
|
|
});
|
|
|
|
it("falls back to the worktree folder name when the repository is unknown", () => {
|
|
const worktrees: Worktree[] = [
|
|
{ id: "wt-1", sessionId: "s-1", repositoryId: "missing", path: "/tasks/x/kandev" },
|
|
];
|
|
const options = buildWorktreeOptions(worktrees, []);
|
|
expect(options).toEqual([{ worktreeId: "wt-1", label: "kandev", branch: undefined }]);
|
|
});
|
|
|
|
it("handles Windows-style paths", () => {
|
|
const worktrees: Worktree[] = [{ id: "wt-1", sessionId: "s-1", path: "C:\\tasks\\x\\kandev" }];
|
|
expect(buildWorktreeOptions(worktrees, [])[0].label).toBe("kandev");
|
|
});
|
|
|
|
it("falls back to a positional label without repository or path", () => {
|
|
const worktrees: Worktree[] = [
|
|
{ id: "wt-1", sessionId: "s-1" },
|
|
{ id: "wt-2", sessionId: "s-1" },
|
|
];
|
|
const options = buildWorktreeOptions(worktrees, []);
|
|
expect(options.map((o) => o.label)).toEqual(["Worktree 1", "Worktree 2"]);
|
|
});
|
|
});
|