diff --git a/ui/litellm-dashboard/src/components/model_add/ChatGPTLoginButton.test.tsx b/ui/litellm-dashboard/src/components/model_add/ChatGPTLoginButton.test.tsx
new file mode 100644
index 00000000000..2950f7f491a
--- /dev/null
+++ b/ui/litellm-dashboard/src/components/model_add/ChatGPTLoginButton.test.tsx
@@ -0,0 +1,26 @@
+import { render, screen } from "@testing-library/react";
+import { describe, expect, it, vi } from "vitest";
+import ChatGPTLoginButton from "./ChatGPTLoginButton";
+
+vi.mock("@/app/(dashboard)/hooks/useAuthorized", () => ({
+ default: () => ({ accessToken: "test-token" }),
+}));
+
+vi.mock("@/components/networking", async () => {
+ const actual = await vi.importActual("@/components/networking");
+ return {
+ ...actual,
+ chatgptOauthStartCall: vi.fn(),
+ chatgptOauthStatusCall: vi.fn(),
+ chatgptOauthCancelCall: vi.fn(),
+ };
+});
+
+describe("ChatGPTLoginButton", () => {
+ it("renders with the ChatGPT provider label", () => {
+ render();
+ expect(
+ screen.getByRole("button", { name: /Sign in with ChatGPT/i }),
+ ).toBeInTheDocument();
+ });
+});
diff --git a/ui/litellm-dashboard/src/components/model_add/CopilotLoginButton.test.tsx b/ui/litellm-dashboard/src/components/model_add/CopilotLoginButton.test.tsx
new file mode 100644
index 00000000000..8a701b1c17b
--- /dev/null
+++ b/ui/litellm-dashboard/src/components/model_add/CopilotLoginButton.test.tsx
@@ -0,0 +1,26 @@
+import { render, screen } from "@testing-library/react";
+import { describe, expect, it, vi } from "vitest";
+import CopilotLoginButton from "./CopilotLoginButton";
+
+vi.mock("@/app/(dashboard)/hooks/useAuthorized", () => ({
+ default: () => ({ accessToken: "test-token" }),
+}));
+
+vi.mock("@/components/networking", async () => {
+ const actual = await vi.importActual("@/components/networking");
+ return {
+ ...actual,
+ copilotOauthStartCall: vi.fn(),
+ copilotOauthStatusCall: vi.fn(),
+ copilotOauthCancelCall: vi.fn(),
+ };
+});
+
+describe("CopilotLoginButton", () => {
+ it("renders with the GitHub Copilot provider label", () => {
+ render();
+ expect(
+ screen.getByRole("button", { name: /Sign in with GitHub Copilot/i }),
+ ).toBeInTheDocument();
+ });
+});
diff --git a/ui/litellm-dashboard/src/components/model_add/OAuthDeviceLoginButton.test.tsx b/ui/litellm-dashboard/src/components/model_add/OAuthDeviceLoginButton.test.tsx
new file mode 100644
index 00000000000..cb874098f2e
--- /dev/null
+++ b/ui/litellm-dashboard/src/components/model_add/OAuthDeviceLoginButton.test.tsx
@@ -0,0 +1,170 @@
+import { act, fireEvent, render, screen, waitFor } from "@testing-library/react";
+import { beforeEach, describe, expect, it, vi } from "vitest";
+import OAuthDeviceLoginButton from "./OAuthDeviceLoginButton";
+
+vi.mock("@/app/(dashboard)/hooks/useAuthorized", () => ({
+ default: () => ({ accessToken: "test-token" }),
+}));
+
+describe("OAuthDeviceLoginButton", () => {
+ beforeEach(() => {
+ vi.useRealTimers();
+ });
+
+ const baseProps = {
+ providerLabel: "ChatGPT",
+ startCall: vi.fn(),
+ statusCall: vi.fn(),
+ cancelCall: vi.fn(),
+ onSuccess: vi.fn(),
+ };
+
+ it("disables Sign-in when no credential name is provided", () => {
+ render();
+ const button = screen.getByRole("button", { name: /Sign in with ChatGPT/i });
+ expect(button).toBeDisabled();
+ });
+
+ it("enables Sign-in once a credential name is provided", () => {
+ render(
+ ,
+ );
+ expect(
+ screen.getByRole("button", { name: /Sign in with ChatGPT/i }),
+ ).toBeEnabled();
+ });
+
+ it("on Sign-in click, shows the user code and verification URL returned by startCall", async () => {
+ const startCall = vi.fn().mockResolvedValue({
+ session_id: "sess-1",
+ user_code: "ABCD-1234",
+ verification_url: "https://auth.example/code",
+ interval: 5,
+ });
+ const statusCall = vi.fn().mockResolvedValue({ status: "pending" });
+
+ render(
+ ,
+ );
+
+ fireEvent.click(screen.getByRole("button", { name: /Sign in with ChatGPT/i }));
+
+ await waitFor(() => {
+ expect(screen.getByText("ABCD-1234")).toBeInTheDocument();
+ });
+ expect(
+ screen.getByRole("link", { name: "https://auth.example/code" }),
+ ).toHaveAttribute("href", "https://auth.example/code");
+ expect(startCall).toHaveBeenCalledWith("test-token", "my-creds");
+ });
+
+ it("calls onSuccess when polling reports success", async () => {
+ const onSuccess = vi.fn();
+ const startCall = vi.fn().mockResolvedValue({
+ session_id: "sess-1",
+ user_code: "CODE",
+ verification_url: "https://auth.example/code",
+ interval: 5,
+ });
+ const statusCall = vi.fn().mockResolvedValue({ status: "success" });
+
+ render(
+ ,
+ );
+ fireEvent.click(screen.getByRole("button", { name: /Sign in with ChatGPT/i }));
+
+ // Poll interval is 3s; allow up to 10s of real time for the first poll
+ // to fire and resolve with status=success.
+ await waitFor(
+ () => {
+ expect(onSuccess).toHaveBeenCalledTimes(1);
+ },
+ { timeout: 10000 },
+ );
+ }, 15000);
+
+ it("surfaces error status from polling", async () => {
+ const startCall = vi.fn().mockResolvedValue({
+ session_id: "sess-1",
+ user_code: "CODE",
+ verification_url: "https://auth.example/code",
+ interval: 5,
+ });
+ const statusCall = vi
+ .fn()
+ .mockResolvedValue({ status: "error", message: "upstream failure" });
+
+ render(
+ ,
+ );
+ fireEvent.click(screen.getByRole("button", { name: /Sign in with ChatGPT/i }));
+
+ await waitFor(
+ () => {
+ expect(screen.getByText("upstream failure")).toBeInTheDocument();
+ },
+ { timeout: 10000 },
+ );
+ }, 15000);
+
+ it("on Cancel click, calls cancelCall and returns to idle state", async () => {
+ const cancelCall = vi.fn().mockResolvedValue(undefined);
+ const startCall = vi.fn().mockResolvedValue({
+ session_id: "sess-1",
+ user_code: "CODE",
+ verification_url: "https://auth.example/code",
+ interval: 5,
+ });
+ const statusCall = vi.fn().mockResolvedValue({ status: "pending" });
+
+ render(
+ ,
+ );
+ fireEvent.click(screen.getByRole("button", { name: /Sign in with ChatGPT/i }));
+ await waitFor(() => {
+ expect(screen.getByText("CODE")).toBeInTheDocument();
+ });
+
+ fireEvent.click(screen.getByRole("button", { name: /Cancel/i }));
+
+ await waitFor(() => {
+ expect(cancelCall).toHaveBeenCalledWith("test-token", "sess-1");
+ });
+ // Back to idle — Sign-in button visible again
+ expect(
+ screen.getByRole("button", { name: /Sign in with ChatGPT/i }),
+ ).toBeInTheDocument();
+ });
+});