diff --git a/ui/litellm-dashboard/src/components/shared/CopyButton.test.tsx b/ui/litellm-dashboard/src/components/shared/CopyButton.test.tsx
index c2c2ee199b4..713a1435da3 100644
--- a/ui/litellm-dashboard/src/components/shared/CopyButton.test.tsx
+++ b/ui/litellm-dashboard/src/components/shared/CopyButton.test.tsx
@@ -3,7 +3,7 @@ import userEvent from "@testing-library/user-event";
import { afterEach, describe, expect, it, vi } from "vitest";
import type { ComponentProps } from "react";
import { copyToClipboard } from "@/utils/dataUtils";
-import { renderWithProviders, screen, waitFor } from "../../../tests/test-utils";
+import { renderWithProviders, screen } from "../../../tests/test-utils";
import CopyButton from "./CopyButton";
vi.mock("@/utils/dataUtils", () => ({ copyToClipboard: vi.fn() }));
@@ -28,55 +28,35 @@ describe("CopyButton", () => {
expect(screen.queryByRole("button")).not.toBeInTheDocument();
});
- it("shows the confirmation checkmark only after a successful write", async () => {
+ it("does not show the checkmark when copying fails", async () => {
const user = userEvent.setup();
- const writeText = vi.fn().mockResolvedValue(undefined);
- Object.defineProperty(navigator, "clipboard", { value: { writeText }, configurable: true });
+ vi.mocked(copyToClipboard).mockResolvedValue(false);
renderWithProviders();
- const button = screen.getByRole("button", { name: "Copy value" });
- expect(screen.getByRole("img", { name: "Copy icon" })).toBeInTheDocument();
+ await user.click(screen.getByRole("button", { name: "Copy value" }));
- await user.click(button);
-
- expect(writeText).toHaveBeenCalledWith("secret-value");
- expect(await screen.findByRole("img", { name: "Copied icon" })).toBeInTheDocument();
- });
-
- it("does not show the checkmark when the clipboard write is rejected", async () => {
- const user = userEvent.setup();
- const writeText = vi.fn().mockRejectedValue(new Error("permission denied"));
- Object.defineProperty(navigator, "clipboard", { value: { writeText }, configurable: true });
-
- renderWithProviders();
-
- const button = screen.getByRole("button", { name: "Copy value" });
- await user.click(button);
-
- await waitFor(() => expect(writeText).toHaveBeenCalledWith("secret-value"));
+ expect(copyToClipboard).toHaveBeenCalledWith("secret-value", "Copied");
expect(screen.queryByRole("img", { name: "Copied icon" })).not.toBeInTheDocument();
expect(screen.getByRole("img", { name: "Copy icon" })).toBeInTheDocument();
});
- it("does not show the checkmark when the clipboard API is unavailable", async () => {
+ it("still copies when the clipboard API is unavailable, as on a plain HTTP origin", async () => {
const user = userEvent.setup();
Object.defineProperty(navigator, "clipboard", { value: undefined, configurable: true });
+ vi.mocked(copyToClipboard).mockResolvedValue(true);
renderWithProviders();
- const button = screen.getByRole("button", { name: "Copy value" });
- await user.click(button);
+ await user.click(screen.getByRole("button", { name: "Copy value" }));
- expect(screen.queryByRole("img", { name: "Copied icon" })).not.toBeInTheDocument();
- expect(screen.getByRole("img", { name: "Copy icon" })).toBeInTheDocument();
- expect(copyToClipboard).not.toHaveBeenCalled();
+ expect(copyToClipboard).toHaveBeenCalledWith("secret-value", "Copied");
+ expect(await screen.findByRole("img", { name: "Copied icon" })).toBeInTheDocument();
});
it("keeps the default icon styling, custom classes, title and 1200ms confirmation", async () => {
vi.useFakeTimers();
- const writeText = vi.fn().mockResolvedValue(undefined);
- Object.defineProperty(navigator, "clipboard", { value: { writeText }, configurable: true });
+ vi.mocked(copyToClipboard).mockResolvedValue(true);
renderWithProviders(
,
);
@@ -87,8 +67,7 @@ describe("CopyButton", () => {
expect(screen.getByRole("img", { name: "Copy icon" })).toHaveClass("size-3");
await act(async () => fireEvent.click(button));
- expect(writeText).toHaveBeenCalledWith("request-id");
- expect(copyToClipboard).not.toHaveBeenCalled();
+ expect(copyToClipboard).toHaveBeenCalledWith("request-id", "Copied");
act(() => vi.advanceTimersByTime(1199));
expect(screen.getByRole("img", { name: "Copied icon" })).toBeInTheDocument();
act(() => vi.advanceTimersByTime(1));
diff --git a/ui/litellm-dashboard/src/components/shared/CopyButton.tsx b/ui/litellm-dashboard/src/components/shared/CopyButton.tsx
index 57ae86a26d0..89f99f1367b 100644
--- a/ui/litellm-dashboard/src/components/shared/CopyButton.tsx
+++ b/ui/litellm-dashboard/src/components/shared/CopyButton.tsx
@@ -53,19 +53,7 @@ const CopyButton: React.FC = ({
if (value == null || (!value && !isAction)) return null;
- const handleCopy = async () => {
- if (isAction) {
- setCopied(await copyToClipboard(value, copiedLabel));
- return;
- }
- if (!navigator.clipboard) return;
- try {
- await navigator.clipboard.writeText(value);
- setCopied(true);
- } catch {
- setCopied(false);
- }
- };
+ const handleCopy = async () => setCopied(await copyToClipboard(value, copiedLabel));
const button = (