ce8b5dc86d
- Extract tool instance lifecycle endpoints (start/stop/restart/delete) from api/tool/tool_instances.py into new api/tool/tool_lifecycle.py. - Register tool_lifecycle_router in main.py and api/tool/__init__.py. - Extract inline WorkspaceDetailPage components into components/features/workspace/: detail header, tab bars, file/git/tools/settings panels. Slim page from ~446 to ~62 lines. - Update OpenSpec reorganize-long-files tasks to reflect completed work and current source state; mark change completed. - Regenerate project maps. Quality gates: python3 -m py_compile (backend clean), npm run typecheck, npm run lint, npm test -- --run (87 passed), pytest workspace integration and unit tests (27 passed, 1 skipped).
135 lines
2.8 KiB
TypeScript
135 lines
2.8 KiB
TypeScript
import "@testing-library/jest-dom/vitest";
|
|
import {
|
|
cleanup,
|
|
fireEvent,
|
|
render,
|
|
screen,
|
|
waitFor,
|
|
} from "@testing-library/react";
|
|
import { MemoryRouter, Routes, Route } from "react-router-dom";
|
|
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
|
|
afterEach(() => {
|
|
cleanup();
|
|
});
|
|
|
|
import { WorkspaceDetailPage } from "./WorkspaceDetailPage";
|
|
|
|
const mockWorkspaces = [
|
|
{
|
|
id: "ws-1",
|
|
name: "dev",
|
|
repo_name: "my-repo",
|
|
project_name: "My Project",
|
|
branch: "main",
|
|
path: "/data/ws/dev",
|
|
status: "ready",
|
|
instance_count: 0,
|
|
},
|
|
];
|
|
|
|
vi.mock("../hooks/use-workspaces", () => ({
|
|
useWorkspaces: () => ({
|
|
workspaces: mockWorkspaces,
|
|
loading: false,
|
|
error: null,
|
|
refresh: vi.fn(),
|
|
}),
|
|
}));
|
|
|
|
vi.mock("../hooks/use-workspace-files", () => ({
|
|
useWorkspaceFiles: () => ({
|
|
entries: [],
|
|
content: null,
|
|
currentPath: "",
|
|
loading: false,
|
|
error: null,
|
|
loadFile: vi.fn(),
|
|
saveFile: vi.fn(),
|
|
navigateTo: vi.fn(),
|
|
}),
|
|
}));
|
|
|
|
vi.mock("../hooks/use-workspace-git", () => ({
|
|
useWorkspaceGit: () => ({
|
|
status: {
|
|
branch: "main",
|
|
modified: [],
|
|
added: [],
|
|
deleted: [],
|
|
untracked: [],
|
|
ahead: 0,
|
|
behind: 0,
|
|
},
|
|
history: [],
|
|
branches: ["main"],
|
|
currentBranch: "main",
|
|
loading: false,
|
|
error: null,
|
|
commit: vi.fn(),
|
|
push: vi.fn(),
|
|
pull: vi.fn(),
|
|
fetch: vi.fn(),
|
|
checkout: vi.fn(),
|
|
}),
|
|
}));
|
|
|
|
vi.mock("../hooks/use-workspace-instances", () => ({
|
|
useWorkspaceInstances: () => ({
|
|
instances: [],
|
|
loading: false,
|
|
error: null,
|
|
refresh: vi.fn(),
|
|
}),
|
|
}));
|
|
|
|
vi.mock("../hooks/use-mobile-viewport", () => ({
|
|
useMobileViewport: () => false,
|
|
}));
|
|
|
|
describe("WorkspaceDetailPage", () => {
|
|
it("renders workspace header and tabs", async () => {
|
|
render(
|
|
<MemoryRouter initialEntries={["/workspaces/ws-1"]}>
|
|
<Routes>
|
|
<Route
|
|
path="/workspaces/:workspaceId"
|
|
element={<WorkspaceDetailPage />}
|
|
/>
|
|
</Routes>
|
|
</MemoryRouter>,
|
|
);
|
|
|
|
await waitFor(() => {
|
|
expect(screen.getByText("dev")).toBeInTheDocument();
|
|
});
|
|
expect(screen.getByText(/my-repo/i)).toBeInTheDocument();
|
|
expect(screen.getByRole("tab", { name: /files/i })).toBeInTheDocument();
|
|
expect(screen.getByRole("tab", { name: /git/i })).toBeInTheDocument();
|
|
expect(screen.getByRole("tab", { name: /tools/i })).toBeInTheDocument();
|
|
expect(screen.getByRole("tab", { name: /settings/i })).toBeInTheDocument();
|
|
});
|
|
|
|
it("switches tabs", async () => {
|
|
render(
|
|
<MemoryRouter initialEntries={["/workspaces/ws-1"]}>
|
|
<Routes>
|
|
<Route
|
|
path="/workspaces/:workspaceId"
|
|
element={<WorkspaceDetailPage />}
|
|
/>
|
|
</Routes>
|
|
</MemoryRouter>,
|
|
);
|
|
|
|
await waitFor(() => {
|
|
expect(
|
|
screen.getByRole("tab", { name: /settings/i }),
|
|
).toBeInTheDocument();
|
|
});
|
|
|
|
fireEvent.click(screen.getByRole("tab", { name: /settings/i }));
|
|
expect(screen.getByText(/workspace info/i)).toBeInTheDocument();
|
|
});
|
|
});
|