diff --git a/src/components/MarketplaceListingArchive.test.tsx b/src/components/MarketplaceListingArchive.test.tsx new file mode 100644 index 00000000..2be524f9 --- /dev/null +++ b/src/components/MarketplaceListingArchive.test.tsx @@ -0,0 +1,52 @@ +import { cleanup, render, screen } from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import { McpListingForm } from "@/components/mcp/McpListingForm"; +import { PromptListingForm } from "@/components/prompts/PromptListingForm"; +import { SkillListingForm } from "@/components/skills/SkillListingForm"; + +const { confirm, push, refresh } = vi.hoisted(() => ({ + confirm: vi.fn(() => Promise.resolve(true)), + push: vi.fn(), + refresh: vi.fn(), +})); + +vi.mock("next/navigation", () => ({ + useRouter: () => ({ push, refresh }), +})); + +vi.mock("@/components/providers/DialogProvider", () => ({ + useDialog: () => ({ confirm }), +})); + +const forms = [ + { name: "skill", component: SkillListingForm }, + { name: "MCP", component: McpListingForm }, + { name: "prompt", component: PromptListingForm }, +]; + +describe("marketplace listing archive errors", () => { + beforeEach(() => { + vi.clearAllMocks(); + vi.stubGlobal("fetch", vi.fn()); + }); + + afterEach(() => { + cleanup(); + vi.unstubAllGlobals(); + }); + + it.each(forms)("shows API errors when the $name archive request fails", async ({ component: Form }) => { + vi.mocked(fetch).mockResolvedValue( + new Response(JSON.stringify({ error: "Archive denied" }), { status: 403 }) + ); + + render(
); + await userEvent.click(screen.getByRole("button", { name: "Archive" })); + + expect(await screen.findByText("Archive denied")).toBeInTheDocument(); + expect(screen.getByRole("button", { name: "Archive" })).toBeEnabled(); + expect(push).not.toHaveBeenCalled(); + expect(refresh).not.toHaveBeenCalled(); + }); +}); diff --git a/src/components/mcp/McpListingForm.tsx b/src/components/mcp/McpListingForm.tsx index f8308d1c..87c052b9 100644 --- a/src/components/mcp/McpListingForm.tsx +++ b/src/components/mcp/McpListingForm.tsx @@ -109,16 +109,22 @@ export function McpListingForm({ slug, initialData }: McpListingFormProps) { if (!await confirm("Archive this MCP server listing? It will be hidden from the marketplace.")) return; setDeleting(true); + setError(null); try { const res = await fetch(`/api/mcp/${slug}`, { method: "DELETE" }); - if (res.ok) { - router.push("/dashboard/mcp"); - router.refresh(); + if (!res.ok) { + const data = await res.json().catch(() => ({})); + setError(data.error || "Failed to archive MCP server listing"); + return; } + + router.push("/dashboard/mcp"); + router.refresh(); } catch { - // ignore + setError("Failed to archive MCP server listing"); + } finally { + setDeleting(false); } - setDeleting(false); } return ( diff --git a/src/components/prompts/PromptListingForm.tsx b/src/components/prompts/PromptListingForm.tsx index a40c0e8f..43718e60 100644 --- a/src/components/prompts/PromptListingForm.tsx +++ b/src/components/prompts/PromptListingForm.tsx @@ -110,16 +110,22 @@ export function PromptListingForm({ slug, initialData }: PromptListingFormProps) if (!await confirm("Archive this prompt listing? It will be hidden from the marketplace.")) return; setDeleting(true); + setError(null); try { const res = await fetch(`/api/prompts/${slug}`, { method: "DELETE" }); - if (res.ok) { - router.push("/dashboard/prompts"); - router.refresh(); + if (!res.ok) { + const data = await res.json().catch(() => ({})); + setError(data.error || "Failed to archive prompt listing"); + return; } + + router.push("/dashboard/prompts"); + router.refresh(); } catch { - // ignore + setError("Failed to archive prompt listing"); + } finally { + setDeleting(false); } - setDeleting(false); } return ( diff --git a/src/components/skills/SkillListingForm.tsx b/src/components/skills/SkillListingForm.tsx index b9802c67..31c08777 100644 --- a/src/components/skills/SkillListingForm.tsx +++ b/src/components/skills/SkillListingForm.tsx @@ -237,16 +237,22 @@ export function SkillListingForm({ slug, listingId, initialData }: SkillListingF if (!await confirm("Archive this skill listing? It will be hidden from the marketplace.")) return; setDeleting(true); + setError(null); try { const res = await fetch(`/api/skills/${slug}`, { method: "DELETE" }); - if (res.ok) { - router.push("/dashboard/skills"); - router.refresh(); + if (!res.ok) { + const data = await res.json().catch(() => ({})); + setError(data.error || "Failed to archive skill listing"); + return; } + + router.push("/dashboard/skills"); + router.refresh(); } catch { - // ignore + setError("Failed to archive skill listing"); + } finally { + setDeleting(false); } - setDeleting(false); } return (