Files
kandev/apps/web/components/task/ensure-session-error.test.tsx
T

118 lines
4.0 KiB
TypeScript

import { describe, it, expect, afterEach, vi } from "vitest";
import { render, screen, cleanup, fireEvent } from "@testing-library/react";
import {
describeEnsureError,
EnsureSessionErrorBanner,
EnsureSessionErrorEmptyState,
} from "./ensure-session-error";
afterEach(cleanup);
describe("describeEnsureError", () => {
it("returns null when there is no error", () => {
expect(describeEnsureError(null)).toBeNull();
});
it("detects the missing-agent-profile case and links to workspace settings", () => {
const info = describeEnsureError(
new Error("agent_profile_id is required to start agent"),
"ws-1",
);
expect(info).not.toBeNull();
expect(info?.isAgentProfileMissing).toBe(true);
expect(info?.action).toEqual({
label: "Open workspace settings",
href: "/settings/workspaces/ws-1",
});
});
it("returns a missing-agent-profile descriptor without an action when workspaceId is absent", () => {
const info = describeEnsureError(new Error("agent_profile_id is required to start agent"));
expect(info?.isAgentProfileMissing).toBe(true);
expect(info?.action).toBeNull();
});
it("falls through to the generic error for unrelated failures", () => {
const info = describeEnsureError(new Error("websocket disconnected"), "ws-1");
expect(info?.isAgentProfileMissing).toBe(false);
expect(info?.title).toBe("Couldn't start a session");
expect(info?.detail).toContain("websocket disconnected");
expect(info?.action).toBeNull();
});
it("does not misclassify unrelated errors that merely mention agent_profile_id", () => {
const info = describeEnsureError(new Error("invalid agent_profile_id format"), "ws-1");
expect(info?.isAgentProfileMissing).toBe(false);
expect(info?.title).toBe("Couldn't start a session");
});
it("uses a fallback detail when the underlying error has no message", () => {
const info = describeEnsureError(new Error(""));
expect(info?.detail).toMatch(/backend rejected/i);
});
});
describe("EnsureSessionErrorBanner", () => {
it("renders nothing when there is no error", () => {
const { container } = render(
<EnsureSessionErrorBanner error={null} onRetry={() => {}} workspaceId="ws-1" />,
);
expect(container.firstChild).toBeNull();
});
it("renders the missing-agent-profile message and a settings link", () => {
render(
<EnsureSessionErrorBanner
error={new Error("agent_profile_id is required to start agent")}
onRetry={() => {}}
workspaceId="ws-1"
/>,
);
expect(screen.getByText("No agent profile configured")).toBeTruthy();
const link = screen.getByTestId("ensure-session-error-action") as HTMLAnchorElement;
expect(link.getAttribute("href")).toBe("/settings/workspaces/ws-1");
});
it("invokes onRetry when the retry button is clicked", () => {
const onRetry = vi.fn();
render(
<EnsureSessionErrorBanner error={new Error("websocket disconnected")} onRetry={onRetry} />,
);
fireEvent.click(screen.getByTestId("ensure-session-error-retry"));
expect(onRetry).toHaveBeenCalledTimes(1);
});
it("disables retry while another recovery action is pending", () => {
const onRetry = vi.fn();
render(
<EnsureSessionErrorBanner
error={new Error("recovery pending")}
onRetry={onRetry}
retryDisabled
/>,
);
const retry = screen.getByTestId("ensure-session-error-retry") as HTMLButtonElement;
expect(retry.disabled).toBe(true);
fireEvent.click(retry);
expect(onRetry).not.toHaveBeenCalled();
});
});
describe("EnsureSessionErrorEmptyState", () => {
it("renders the centered preview-ensure-error layout with a retry", () => {
const onRetry = vi.fn();
render(
<EnsureSessionErrorEmptyState
error={new Error("boom")}
onRetry={onRetry}
workspaceId={null}
/>,
);
expect(screen.getByTestId("preview-ensure-error")).toBeTruthy();
fireEvent.click(screen.getByTestId("ensure-session-error-retry"));
expect(onRetry).toHaveBeenCalledTimes(1);
});
});