Files
kandev/apps/web/components/task/file-browser-toolbar.test.tsx
T
Carlos Florêncio bc218dfbbe feat: add workspace sources from files panel (#1900)
* 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
2026-07-25 21:29:53 +01:00

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();
});
});