bc218dfbbe
* feat: add workspace sources from files panel * fix: preserve worktree identity during recovery * feat: unify workspace source actions * fix: validate recovered workspace repositories * fix: harden workspace source filesystem boundaries * test: split workspace repository chip coverage * fix: restrict repository rescan recovery paths * fix: stabilize workspace source CI checks * test: canonicalize workspace source paths * test: align workspace source container paths * fix: populate direct workspace repository names * test: preserve git fixture rewrites across restarts * test: isolate backend git fixture config * fix: resume remote workspace executions before creation * fix: preserve ssh agent identity during resume * fix: preserve SSH workspace across backend restart * fix: simplify workspace actions trigger * fix: refine add workspace repositories UX * docs: narrow workspace source UX updates * fix: avoid phantom turn on session resume * test(web): normalize workspace source request headers * feat(web): streamline workspace source picker * docs: remove trailing spaces from workspace source plan * fix: prevent resume status from creating phantom turns * fix(web): stabilize workspace source attachment * refactor(web): extract workspace source store contract * fix: avoid phantom turns on idle agent resume * fix: address backend static analysis findings * fix: preserve agentctl readiness status value * fix: preserve resume messages without active turns * fix: resume agents from promoted workspace root * chore: merge origin/main into feature/add-new-repo-folder-d97 * feat(web): explain workspace source consequences * test(websocket): count session activity subscription
122 lines
4.1 KiB
TypeScript
122 lines
4.1 KiB
TypeScript
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
import { cleanup, fireEvent, render, screen, waitFor } from "@testing-library/react";
|
|
import { TooltipProvider } from "@kandev/ui/tooltip";
|
|
import { FileBrowserToolbar } from "./file-browser-toolbar";
|
|
|
|
let isMobile = false;
|
|
|
|
vi.mock("@/hooks/use-responsive-breakpoint", () => ({
|
|
useResponsiveBreakpoint: () => ({ isMobile }),
|
|
}));
|
|
|
|
afterEach(() => {
|
|
cleanup();
|
|
isMobile = false;
|
|
});
|
|
|
|
describe("FileBrowserToolbar workspace actions", () => {
|
|
it("keeps Open workspace folder available while Add sources explains why it is unavailable", () => {
|
|
const onAddSources = vi.fn();
|
|
const onOpenFolder = vi.fn();
|
|
|
|
render(
|
|
<TooltipProvider>
|
|
<FileBrowserToolbar
|
|
displayPath="workspace"
|
|
fullPath="/workspace"
|
|
copied={false}
|
|
expandedPathsSize={0}
|
|
onCopyPath={vi.fn()}
|
|
onOpenFolder={onOpenFolder}
|
|
onStartSearch={vi.fn()}
|
|
onCollapseAll={vi.fn()}
|
|
showCreateButton={false}
|
|
onAddSources={onAddSources}
|
|
addSourcesDisabledReason="Wait for the active agent turn to finish"
|
|
/>
|
|
</TooltipProvider>,
|
|
);
|
|
|
|
const trigger = screen.getByRole("button", { name: "Workspace actions" });
|
|
fireEvent.pointerDown(trigger, { button: 0, ctrlKey: false });
|
|
fireEvent.click(trigger);
|
|
|
|
const addSources = screen.getByRole("menuitem", {
|
|
name: /Add Repositories to workspace/i,
|
|
});
|
|
expect(addSources.hasAttribute("data-disabled")).toBe(true);
|
|
expect(addSources.className).toContain("min-h-11");
|
|
expect(screen.getByText("Wait for the active agent turn to finish")).toBeTruthy();
|
|
fireEvent.click(screen.getByRole("menuitem", { name: "Open workspace folder" }));
|
|
expect(onOpenFolder).toHaveBeenCalledOnce();
|
|
expect(onAddSources).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("opens Add sources only after the menu closes and uses the combined trigger as its opener", async () => {
|
|
const onAddSources = vi.fn();
|
|
const openerRef = { current: null as HTMLButtonElement | null };
|
|
|
|
render(
|
|
<TooltipProvider>
|
|
<FileBrowserToolbar
|
|
displayPath="workspace"
|
|
fullPath="/workspace"
|
|
copied={false}
|
|
expandedPathsSize={0}
|
|
onCopyPath={vi.fn()}
|
|
onOpenFolder={vi.fn()}
|
|
onStartSearch={vi.fn()}
|
|
onCollapseAll={vi.fn()}
|
|
showCreateButton={false}
|
|
onAddSources={onAddSources}
|
|
addSourcesButtonRef={openerRef}
|
|
/>
|
|
</TooltipProvider>,
|
|
);
|
|
|
|
const trigger = screen.getByRole("button", { name: "Workspace actions" });
|
|
fireEvent.pointerDown(trigger, { button: 0, ctrlKey: false });
|
|
fireEvent.click(trigger);
|
|
fireEvent.click(screen.getByRole("menuitem", { name: "Add Repositories to workspace" }));
|
|
|
|
await waitFor(() => expect(onAddSources).toHaveBeenCalledWith(trigger));
|
|
expect(openerRef.current).toBe(trigger);
|
|
});
|
|
|
|
it("restores the mobile trigger after the source Drawer finishes closing", async () => {
|
|
isMobile = true;
|
|
const onAddSources = vi.fn();
|
|
render(
|
|
<TooltipProvider>
|
|
<FileBrowserToolbar
|
|
displayPath="workspace"
|
|
fullPath="/workspace"
|
|
copied={false}
|
|
expandedPathsSize={0}
|
|
onCopyPath={vi.fn()}
|
|
onOpenFolder={vi.fn()}
|
|
onStartSearch={vi.fn()}
|
|
onCollapseAll={vi.fn()}
|
|
showCreateButton={false}
|
|
onAddSources={onAddSources}
|
|
/>
|
|
</TooltipProvider>,
|
|
);
|
|
|
|
const trigger = screen.getByRole("button", { name: "Workspace actions" });
|
|
fireEvent.pointerDown(trigger, { button: 0, ctrlKey: false });
|
|
fireEvent.click(trigger);
|
|
fireEvent.click(screen.getByRole("menuitem", { name: "Add Repositories to workspace" }));
|
|
await waitFor(() => expect(onAddSources).toHaveBeenCalledOnce());
|
|
|
|
const drawer = document.createElement("div");
|
|
drawer.dataset.testid = "add-workspace-sources-drawer";
|
|
drawer.dataset.state = "closed";
|
|
document.body.append(drawer);
|
|
fireEvent.animationEnd(drawer);
|
|
|
|
await waitFor(() => expect(document.activeElement).toBe(trigger));
|
|
drawer.remove();
|
|
});
|
|
});
|