From 0da1d4277f64ec627701c3d660ee9d16e22ace4e Mon Sep 17 00:00:00 2001
From: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
Date: Thu, 10 Sep 2026 14:55:38 -0300
Subject: [PATCH] fix(dashboard): refresh the providers list after deleting a
compatible provider node (#12298)
---
.../12298-provider-node-delete-refresh.md | 1 +
.../[id]/components/CompatibleNodeCard.tsx | 1 +
...le-node-card-delete-refresh-12298.test.tsx | 135 ++++++++++++++++++
3 files changed, 137 insertions(+)
create mode 100644 changelog.d/fixes/12298-provider-node-delete-refresh.md
create mode 100644 tests/unit/ui/compatible-node-card-delete-refresh-12298.test.tsx
diff --git a/changelog.d/fixes/12298-provider-node-delete-refresh.md b/changelog.d/fixes/12298-provider-node-delete-refresh.md
new file mode 100644
index 0000000000..c902e6cc7b
--- /dev/null
+++ b/changelog.d/fixes/12298-provider-node-delete-refresh.md
@@ -0,0 +1 @@
+- fix(dashboard): refresh the providers list after deleting a compatible provider node (#12298)
diff --git a/src/app/(dashboard)/dashboard/providers/[id]/components/CompatibleNodeCard.tsx b/src/app/(dashboard)/dashboard/providers/[id]/components/CompatibleNodeCard.tsx
index 5347c075a8..144023a4fa 100644
--- a/src/app/(dashboard)/dashboard/providers/[id]/components/CompatibleNodeCard.tsx
+++ b/src/app/(dashboard)/dashboard/providers/[id]/components/CompatibleNodeCard.tsx
@@ -110,6 +110,7 @@ export default function CompatibleNodeCard({
});
if (res.ok) {
router.push("/dashboard/providers");
+ router.refresh();
}
} catch (error) {
console.error("Error deleting provider node:", error);
diff --git a/tests/unit/ui/compatible-node-card-delete-refresh-12298.test.tsx b/tests/unit/ui/compatible-node-card-delete-refresh-12298.test.tsx
new file mode 100644
index 0000000000..91eaf6f319
--- /dev/null
+++ b/tests/unit/ui/compatible-node-card-delete-refresh-12298.test.tsx
@@ -0,0 +1,135 @@
+// @vitest-environment jsdom
+import React, { act } from "react";
+import { createRoot } from "react-dom/client";
+import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
+
+import CompatibleNodeCard from "../../../src/app/(dashboard)/dashboard/providers/[id]/components/CompatibleNodeCard";
+
+const router = vi.hoisted(() => ({
+ push: vi.fn(),
+ refresh: vi.fn(),
+}));
+
+vi.mock("next/navigation", () => ({
+ useRouter: () => router,
+}));
+
+vi.mock("@/shared/components", () => ({
+ Card: ({ children }: { children: React.ReactNode }) =>
{children}
,
+ Button: ({
+ children,
+ onClick,
+ }: {
+ children: React.ReactNode;
+ onClick?: React.MouseEventHandler;
+ }) => ,
+}));
+
+vi.mock("@/shared/components/ProviderIcon", () => ({
+ default: () => null,
+}));
+
+function renderCard(container: HTMLDivElement) {
+ const root = createRoot(container);
+ return root;
+}
+
+describe("CompatibleNodeCard provider deletion (#12298)", () => {
+ let container: HTMLDivElement;
+ let root: ReturnType;
+
+ beforeEach(() => {
+ (
+ globalThis as typeof globalThis & { IS_REACT_ACT_ENVIRONMENT?: boolean }
+ ).IS_REACT_ACT_ENVIRONMENT = true;
+ router.push.mockClear();
+ router.refresh.mockClear();
+ vi.stubGlobal("confirm", vi.fn(() => true));
+
+ container = document.createElement("div");
+ document.body.appendChild(container);
+ root = renderCard(container);
+ });
+
+ afterEach(() => {
+ act(() => root.unmount());
+ container.remove();
+ vi.unstubAllGlobals();
+ });
+
+ async function clickDelete() {
+ await act(async () => {
+ root.render(
+ callback()}
+ openApiKeyAddFlow={vi.fn()}
+ onOpenEditNodeModal={vi.fn()}
+ t={(key) => key}
+ />
+ );
+ });
+
+ const deleteButton = Array.from(container.querySelectorAll("button")).find(
+ (button) => button.textContent === "delete"
+ );
+ expect(deleteButton).toBeDefined();
+
+ await act(async () => {
+ deleteButton?.click();
+ await Promise.resolve();
+ await Promise.resolve();
+ });
+
+ return deleteButton;
+ }
+
+ it("invalidates the cached providers page after a successful delete and navigation", async () => {
+ vi.stubGlobal("fetch", vi.fn().mockResolvedValue({ ok: true } as Response));
+
+ await clickDelete();
+
+ expect(fetch).toHaveBeenCalledWith("/api/provider-nodes/custom-node", {
+ method: "DELETE",
+ });
+ expect(router.push).toHaveBeenCalledWith("/dashboard/providers");
+ expect(router.refresh).toHaveBeenCalledTimes(1);
+ expect(router.push.mock.invocationCallOrder[0]).toBeLessThan(
+ router.refresh.mock.invocationCallOrder[0]
+ );
+ });
+
+ it("does not navigate or refresh when the user cancels the confirm dialog", async () => {
+ vi.stubGlobal("confirm", vi.fn(() => false));
+ vi.stubGlobal("fetch", vi.fn().mockResolvedValue({ ok: true } as Response));
+
+ await clickDelete();
+
+ expect(fetch).not.toHaveBeenCalled();
+ expect(router.push).not.toHaveBeenCalled();
+ expect(router.refresh).not.toHaveBeenCalled();
+ });
+
+ it("does not navigate or refresh when the DELETE response is not ok", async () => {
+ vi.stubGlobal("fetch", vi.fn().mockResolvedValue({ ok: false } as Response));
+
+ await clickDelete();
+
+ expect(router.push).not.toHaveBeenCalled();
+ expect(router.refresh).not.toHaveBeenCalled();
+ });
+
+ it("does not navigate or refresh when the DELETE request throws", async () => {
+ vi.stubGlobal("fetch", vi.fn().mockRejectedValue(new Error("network down")));
+ vi.spyOn(console, "error").mockImplementation(() => {});
+
+ await clickDelete();
+
+ expect(router.push).not.toHaveBeenCalled();
+ expect(router.refresh).not.toHaveBeenCalled();
+ });
+});