chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,202 @@
|
||||
// @vitest-environment jsdom
|
||||
import React from "react";
|
||||
import { act } from "react";
|
||||
import { createRoot } from "react-dom/client";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
// ── Mocks ─────────────────────────────────────────────────────────────────────
|
||||
|
||||
vi.mock("next/link", () => ({
|
||||
default: ({
|
||||
href,
|
||||
children,
|
||||
...props
|
||||
}: React.AnchorHTMLAttributes<HTMLAnchorElement> & { href: string }) => (
|
||||
<a href={href} {...props}>
|
||||
{children}
|
||||
</a>
|
||||
),
|
||||
}));
|
||||
|
||||
vi.mock("next-intl", () => ({
|
||||
useTranslations: () => (key: string) => key,
|
||||
useLocale: () => "en",
|
||||
}));
|
||||
|
||||
// Stub fetch globally
|
||||
const mockFetch = vi.fn().mockResolvedValue({
|
||||
ok: true,
|
||||
json: async () => ({ connections: [], keys: [], data: [], cloudEnabled: false }),
|
||||
});
|
||||
vi.stubGlobal("fetch", mockFetch);
|
||||
|
||||
// Stub next/navigation
|
||||
vi.mock("next/navigation", () => ({
|
||||
notFound: () => {
|
||||
throw new Error("NOT_FOUND");
|
||||
},
|
||||
}));
|
||||
|
||||
// Stub CLI_TOOLS catalog
|
||||
vi.mock("@/shared/constants/cliTools", () => ({
|
||||
CLI_TOOLS: {
|
||||
claude: {
|
||||
id: "claude",
|
||||
name: "Claude Code",
|
||||
icon: "terminal",
|
||||
color: "#D97757",
|
||||
category: "code",
|
||||
configType: "env",
|
||||
vendor: "Anthropic",
|
||||
baseUrlSupport: "full",
|
||||
defaultModels: [],
|
||||
},
|
||||
codex: {
|
||||
id: "codex",
|
||||
name: "Codex",
|
||||
icon: "terminal",
|
||||
color: "#000",
|
||||
category: "code",
|
||||
configType: "custom",
|
||||
vendor: "OpenAI",
|
||||
baseUrlSupport: "full",
|
||||
defaultModels: [],
|
||||
},
|
||||
custom: {
|
||||
id: "custom",
|
||||
name: "Custom CLI",
|
||||
icon: "terminal",
|
||||
color: "#888",
|
||||
category: "code",
|
||||
configType: "custom-builder",
|
||||
vendor: undefined,
|
||||
baseUrlSupport: "full",
|
||||
defaultModels: [],
|
||||
},
|
||||
"hermes-agent": {
|
||||
id: "hermes-agent",
|
||||
name: "Hermes Agent",
|
||||
icon: "terminal",
|
||||
color: "#5865f2",
|
||||
category: "agent",
|
||||
configType: "custom",
|
||||
vendor: "HermesAI",
|
||||
baseUrlSupport: "full",
|
||||
defaultModels: [],
|
||||
},
|
||||
forge: {
|
||||
id: "forge",
|
||||
name: "Forge",
|
||||
icon: "terminal",
|
||||
color: "#888",
|
||||
category: "code",
|
||||
configType: "custom",
|
||||
vendor: undefined,
|
||||
baseUrlSupport: "partial",
|
||||
defaultModels: [],
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
// Stub model constants
|
||||
vi.mock("@/shared/constants/models", () => ({
|
||||
PROVIDER_ID_TO_ALIAS: {},
|
||||
getModelsByProviderId: () => [],
|
||||
}));
|
||||
|
||||
// Stub specialized cards — render a testid so we can identify which was rendered
|
||||
vi.mock("../../../src/app/(dashboard)/dashboard/cli-code/components/index", () => ({
|
||||
ClaudeToolCard: () => <div data-testid="ClaudeToolCard" />,
|
||||
CodexToolCard: () => <div data-testid="CodexToolCard" />,
|
||||
DroidToolCard: () => <div data-testid="DroidToolCard" />,
|
||||
OpenClawToolCard: () => <div data-testid="OpenClawToolCard" />,
|
||||
ClineToolCard: () => <div data-testid="ClineToolCard" />,
|
||||
KiloToolCard: () => <div data-testid="KiloToolCard" />,
|
||||
DefaultToolCard: ({ toolId }: { toolId: string }) => (
|
||||
<div data-testid="DefaultToolCard" data-toolid={toolId} />
|
||||
),
|
||||
AntigravityToolCard: () => <div data-testid="AntigravityToolCard" />,
|
||||
CopilotToolCard: () => <div data-testid="CopilotToolCard" />,
|
||||
CustomCliCard: () => <div data-testid="CustomCliCard" />,
|
||||
HermesAgentToolCard: () => <div data-testid="HermesAgentToolCard" />,
|
||||
}));
|
||||
|
||||
vi.mock("../../../src/app/(dashboard)/dashboard/cli-code/components/CliproxyapiToolCard", () => ({
|
||||
default: () => <div data-testid="CliproxyapiToolCard" />,
|
||||
}));
|
||||
|
||||
// ── Import after mocks ────────────────────────────────────────────────────────
|
||||
|
||||
const { default: ToolDetailClient } = await import(
|
||||
"@/app/(dashboard)/dashboard/cli-code/components/ToolDetailClient"
|
||||
);
|
||||
|
||||
// ── Helpers ───────────────────────────────────────────────────────────────────
|
||||
|
||||
const containers: HTMLElement[] = [];
|
||||
|
||||
function renderDetail(toolId: string, category: "code" | "agent"): HTMLElement {
|
||||
const container = document.createElement("div");
|
||||
document.body.appendChild(container);
|
||||
containers.push(container);
|
||||
|
||||
const root = createRoot(container);
|
||||
act(() => {
|
||||
root.render(<ToolDetailClient toolId={toolId} category={category} />);
|
||||
});
|
||||
return container;
|
||||
}
|
||||
|
||||
// ── Lifecycle ─────────────────────────────────────────────────────────────────
|
||||
|
||||
beforeEach(() => {
|
||||
(
|
||||
globalThis as typeof globalThis & { IS_REACT_ACT_ENVIRONMENT?: boolean }
|
||||
).IS_REACT_ACT_ENVIRONMENT = true;
|
||||
mockFetch.mockClear();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
while (containers.length > 0) {
|
||||
containers.pop()?.remove();
|
||||
}
|
||||
document.body.innerHTML = "";
|
||||
});
|
||||
|
||||
// ── Tests ─────────────────────────────────────────────────────────────────────
|
||||
|
||||
describe("ToolDetailClient", () => {
|
||||
it("renders ClaudeToolCard for toolId=claude", async () => {
|
||||
const container = renderDetail("claude", "code");
|
||||
// Wait for async state resolution
|
||||
await act(async () => {});
|
||||
expect(container.querySelector("[data-testid='ClaudeToolCard']")).not.toBeNull();
|
||||
});
|
||||
|
||||
it("renders CodexToolCard for toolId=codex", async () => {
|
||||
const container = renderDetail("codex", "code");
|
||||
await act(async () => {});
|
||||
expect(container.querySelector("[data-testid='CodexToolCard']")).not.toBeNull();
|
||||
});
|
||||
|
||||
it("renders CustomCliCard for toolId=custom", async () => {
|
||||
const container = renderDetail("custom", "code");
|
||||
await act(async () => {});
|
||||
expect(container.querySelector("[data-testid='CustomCliCard']")).not.toBeNull();
|
||||
});
|
||||
|
||||
it("renders DefaultToolCard for unknown tool (forge, configType:custom)", async () => {
|
||||
const container = renderDetail("forge", "code");
|
||||
await act(async () => {});
|
||||
const card = container.querySelector("[data-testid='DefaultToolCard']");
|
||||
expect(card).not.toBeNull();
|
||||
expect(card!.getAttribute("data-toolid")).toBe("forge");
|
||||
});
|
||||
|
||||
it("renders nothing (null) for completely unknown toolId", async () => {
|
||||
const container = renderDetail("totally-unknown-xyz", "code");
|
||||
await act(async () => {});
|
||||
// CLI_TOOLS["totally-unknown-xyz"] is undefined → returns null → empty container
|
||||
expect(container.textContent).toBe("");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user