From b3aa78da272b7d32662dea7cf5af5451db2524d2 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Tue, 28 Jul 2026 12:14:14 +0000 Subject: [PATCH] fix(ui): apply the custom favicon by removing the competing app/favicon.ico --- .../{src/app => public}/favicon.ico | Bin ui/litellm-dashboard/src/app/layout.test.tsx | 28 ++++++++++++++++++ 2 files changed, 28 insertions(+) rename ui/litellm-dashboard/{src/app => public}/favicon.ico (100%) create mode 100644 ui/litellm-dashboard/src/app/layout.test.tsx diff --git a/ui/litellm-dashboard/src/app/favicon.ico b/ui/litellm-dashboard/public/favicon.ico similarity index 100% rename from ui/litellm-dashboard/src/app/favicon.ico rename to ui/litellm-dashboard/public/favicon.ico diff --git a/ui/litellm-dashboard/src/app/layout.test.tsx b/ui/litellm-dashboard/src/app/layout.test.tsx new file mode 100644 index 00000000000..dfb097b076d --- /dev/null +++ b/ui/litellm-dashboard/src/app/layout.test.tsx @@ -0,0 +1,28 @@ +import fs from "node:fs"; +import path from "node:path"; + +import { describe, expect, it, vi } from "vitest"; + +vi.mock("./globals.css", () => ({})); +vi.mock("next/font/google", () => ({ + Inter: () => ({ className: "inter" }), +})); + +const { metadata } = await import("./layout"); + +const appDir = path.dirname(new URL(import.meta.url).pathname); +const dashboardDir = path.resolve(appDir, "..", ".."); + +describe("root layout favicon", () => { + it("points the only declared icon at the proxy's /get_favicon endpoint", () => { + expect(metadata.icons).toEqual({ icon: "/get_favicon" }); + }); + + it("keeps favicon.ico out of the app directory so Next does not emit a competing icon link", () => { + expect(fs.existsSync(path.join(appDir, "favicon.ico"))).toBe(false); + }); + + it("ships the default favicon as a public asset so the exported build still serves favicon.ico", () => { + expect(fs.existsSync(path.join(dashboardDir, "public", "favicon.ico"))).toBe(true); + }); +});