openclaw/apps/web/app/components/workspace/profile-switcher.test.tsx
kumarabhirup f6eee0b398
refactor(cli): update workspace-seed with dynamic identity and dench skill
Build identity template with workspace path; add seedDenchSkill for skills/dench.
2026-03-03 13:47:38 -08:00

166 lines
5.4 KiB
TypeScript

// @vitest-environment jsdom
import React from "react";
import { describe, expect, it, vi, beforeEach, afterEach } from "vitest";
import { render, screen, waitFor } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { ProfileSwitcher } from "./profile-switcher";
function jsonResponse(body: unknown, status = 200): Response {
return new Response(JSON.stringify(body), {
status,
headers: { "Content-Type": "application/json" },
});
}
describe("ProfileSwitcher workspace delete action", () => {
const originalConfirm = window.confirm;
beforeEach(() => {
vi.restoreAllMocks();
});
afterEach(() => {
window.confirm = originalConfirm;
});
it("deletes a workspace from the dropdown action", async () => {
const user = userEvent.setup();
const onWorkspaceDelete = vi.fn();
let listFetchCount = 0;
global.fetch = vi.fn(async (input: RequestInfo | URL, init?: RequestInit) => {
const url = typeof input === "string" ? input : (input as URL).href;
const method = init?.method ?? "GET";
if (url === "/api/workspace/list" && method === "GET") {
listFetchCount += 1;
if (listFetchCount === 1) {
return jsonResponse({
activeWorkspace: "work",
workspaces: [
{
name: "work",
stateDir: "/home/testuser/.openclaw-work",
workspaceDir: "/home/testuser/.openclaw-work/workspace",
isActive: true,
hasConfig: true,
},
],
activeProfile: "work",
profiles: [
{
name: "work",
stateDir: "/home/testuser/.openclaw-work",
workspaceDir: "/home/testuser/.openclaw-work/workspace",
isActive: true,
hasConfig: true,
},
],
});
}
return jsonResponse({
activeWorkspace: "work",
workspaces: [
{
name: "work",
stateDir: "/home/testuser/.openclaw-work",
workspaceDir: null,
isActive: true,
hasConfig: true,
},
],
activeProfile: "work",
profiles: [
{
name: "work",
stateDir: "/home/testuser/.openclaw-work",
workspaceDir: null,
isActive: true,
hasConfig: true,
},
],
});
}
if (url === "/api/workspace/delete" && method === "POST") {
return jsonResponse({ deleted: true, workspace: "work" });
}
throw new Error(`Unexpected fetch call: ${method} ${url}`);
}) as typeof fetch;
window.confirm = vi.fn(() => true);
render(<ProfileSwitcher onWorkspaceDelete={onWorkspaceDelete} />);
await waitFor(() => {
expect(global.fetch).toHaveBeenCalledWith("/api/workspace/list");
});
await user.click(screen.getByTitle("Switch workspace"));
await user.click(screen.getByTitle("Delete workspace work"));
expect(window.confirm).toHaveBeenCalledTimes(1);
const confirmMsg = vi.mocked(window.confirm).mock.calls[0]?.[0] as string;
expect(confirmMsg).toContain("Delete workspace");
expect(confirmMsg).toContain("work");
expect(confirmMsg).not.toContain("uninstall --workspace --state");
await waitFor(() => {
expect(onWorkspaceDelete).toHaveBeenCalledWith("work");
});
const deleteCall = vi
.mocked(global.fetch)
.mock.calls.find((call) => (typeof call[0] === "string" ? call[0] : (call[0] as URL).href) === "/api/workspace/delete");
expect(deleteCall).toBeTruthy();
expect(deleteCall?.[1]).toMatchObject({
method: "POST",
});
const deleteBody = JSON.parse(deleteCall?.[1]?.body as string);
expect(deleteBody).toMatchObject({ workspace: "work" });
});
it("falls back to a real workspace when API returns stale active workspace", async () => {
const user = userEvent.setup();
global.fetch = vi.fn(async (input: RequestInfo | URL, init?: RequestInit) => {
const url = typeof input === "string" ? input : (input as URL).href;
const method = init?.method ?? "GET";
if (url === "/api/workspace/list" && method === "GET") {
return jsonResponse({
activeWorkspace: "ghost",
workspaces: [
{
name: "ghost",
stateDir: "/home/testuser/.openclaw-ironclaw",
workspaceDir: null,
isActive: true,
hasConfig: true,
},
{
name: "ironclaw",
stateDir: "/home/testuser/.openclaw-ironclaw",
workspaceDir: "/home/testuser/.openclaw-ironclaw/workspace",
isActive: false,
hasConfig: true,
},
],
});
}
throw new Error(`Unexpected fetch call: ${method} ${url}`);
}) as typeof fetch;
render(<ProfileSwitcher />);
await waitFor(() => {
expect(global.fetch).toHaveBeenCalledWith("/api/workspace/list");
});
await waitFor(() => {
expect(screen.getByText("ironclaw")).toBeInTheDocument();
expect(screen.queryByText("ghost")).not.toBeInTheDocument();
});
await user.click(screen.getByTitle("Switch workspace"));
expect(screen.queryByTitle("Delete workspace ghost")).not.toBeInTheDocument();
expect(screen.getByTitle("Delete workspace ironclaw")).toBeInTheDocument();
});
});