166 lines
5.4 KiB
TypeScript
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();
|
|
});
|
|
});
|