diff --git a/ui/litellm-dashboard/src/app/(dashboard)/teams/_components/default-team-settings/DefaultTeamSettingsForm.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/teams/_components/default-team-settings/DefaultTeamSettingsForm.test.tsx index a80eb4fbe1e..7f9e9b4c55b 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/teams/_components/default-team-settings/DefaultTeamSettingsForm.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/teams/_components/default-team-settings/DefaultTeamSettingsForm.test.tsx @@ -1,5 +1,5 @@ import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; -import { fireEvent, render, screen, waitFor } from "@testing-library/react"; +import { render, screen, waitFor } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import React from "react"; import { beforeEach, describe, expect, it, vi } from "vitest"; @@ -139,13 +139,28 @@ describe("DefaultTeamSettingsForm", () => { const { updateSettings } = renderForm(); await enterEditMode(user); - fireEvent.change(await screen.findByLabelText("TPM Limit"), { target: { value: "12.5" } }); + await user.clear(await screen.findByLabelText("TPM Limit")); + await user.type(screen.getByLabelText("TPM Limit"), "12.5"); await user.click(await saveButton()); expect(await screen.findByText("Must be a non-negative whole number")).toBeInTheDocument(); expect(updateSettings).not.toHaveBeenCalled(); }); + it("keeps number-ish junk visible and rejects it instead of silently clearing the limit", async () => { + const user = userEvent.setup(); + const { updateSettings } = renderForm(); + + await enterEditMode(user); + await user.clear(await screen.findByLabelText("TPM Limit")); + await user.type(screen.getByLabelText("TPM Limit"), "12e"); + await user.click(await saveButton()); + + expect(screen.getByLabelText("TPM Limit")).toHaveValue("12e"); + expect(await screen.findByText("Must be a non-negative whole number")).toBeInTheDocument(); + expect(updateSettings).not.toHaveBeenCalled(); + }); + it("sends the models selection through unchanged, sentinel values included", async () => { const user = userEvent.setup(); const { updateSettings } = renderForm(); @@ -223,7 +238,7 @@ describe("DefaultTeamSettingsForm", () => { expect(NotificationsManager.fromBackend).toHaveBeenCalledWith("Set `'STORE_MODEL_IN_DB='True'` in your env."), ); expect(await saveButton()).toBeEnabled(); - expect(screen.getByLabelText("Max Budget (USD)")).toHaveValue(250); + expect(screen.getByLabelText("Max Budget (USD)")).toHaveValue("250"); }); it("discards edits and returns to the read-only view when Cancel is pressed", async () => { @@ -240,7 +255,7 @@ describe("DefaultTeamSettingsForm", () => { expect(updateSettings).not.toHaveBeenCalled(); await enterEditMode(user); - expect(screen.getByLabelText("Max Budget (USD)")).toHaveValue(100); + expect(screen.getByLabelText("Max Budget (USD)")).toHaveValue("100"); expect(await saveButton()).toBeDisabled(); }); }); diff --git a/ui/litellm-dashboard/src/app/(dashboard)/teams/_components/default-team-settings/DefaultTeamSettingsForm.tsx b/ui/litellm-dashboard/src/app/(dashboard)/teams/_components/default-team-settings/DefaultTeamSettingsForm.tsx index 56a7cc13844..43ec838dc59 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/teams/_components/default-team-settings/DefaultTeamSettingsForm.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/teams/_components/default-team-settings/DefaultTeamSettingsForm.tsx @@ -175,7 +175,7 @@ const SettingsForm = ({ initialValues, updateSettings, onCancel, onSaved }: Sett label="Max Budget (USD)" description="Default maximum budget for new teams" > - {({ ref, ...field }) => } + {({ ref, ...field }) => } - {({ ref, ...field }) => } + {({ ref, ...field }) => } - {({ ref, ...field }) => } + {({ ref, ...field }) => } { expect(updateSettings).toHaveBeenCalledWith({ ...SAVED_BODY, max_budget: null }); }); + it("keeps number-ish junk visible and rejects it instead of silently clearing the budget", async () => { + const user = userEvent.setup(); + const { updateSettings } = renderForm(); + + await enterEditMode(user); + await user.clear(await screen.findByLabelText("Max Budget (USD)")); + await user.type(screen.getByLabelText("Max Budget (USD)"), "12e"); + await user.click(await saveButton()); + + expect(screen.getByLabelText("Max Budget (USD)")).toHaveValue("12e"); + expect(await screen.findByText("Must be a non-negative number")).toBeInTheDocument(); + expect(updateSettings).not.toHaveBeenCalled(); + }); + it("sends the models selection through unchanged, sentinel values included", async () => { const user = userEvent.setup(); const { updateSettings } = renderForm(); @@ -275,7 +289,7 @@ describe("DefaultUserSettingsForm", () => { expect(NotificationsManager.fromBackend).toHaveBeenCalledWith("Team(s) not found: team-alhpa."), ); expect(await saveButton()).toBeEnabled(); - expect(screen.getByLabelText("Max Budget (USD)")).toHaveValue(250); + expect(screen.getByLabelText("Max Budget (USD)")).toHaveValue("250"); }); it("discards edits and returns to the read-only view when Cancel is pressed", async () => { @@ -293,7 +307,7 @@ describe("DefaultUserSettingsForm", () => { expect(updateSettings).not.toHaveBeenCalled(); await enterEditMode(user); - expect(screen.getByLabelText("Max Budget (USD)")).toHaveValue(100); + expect(screen.getByLabelText("Max Budget (USD)")).toHaveValue("100"); expect(await saveButton()).toBeDisabled(); }); }); diff --git a/ui/litellm-dashboard/src/app/(dashboard)/users/_components/default-user-settings/DefaultUserSettingsForm.tsx b/ui/litellm-dashboard/src/app/(dashboard)/users/_components/default-user-settings/DefaultUserSettingsForm.tsx index 14520b71b31..c2fbcdb1112 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/users/_components/default-user-settings/DefaultUserSettingsForm.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/users/_components/default-user-settings/DefaultUserSettingsForm.tsx @@ -124,7 +124,7 @@ const TeamsField = ({ control }: { control: SettingsControl }) => { {({ ref, ...budgetField }) => ( - + )} @@ -244,7 +244,7 @@ const SettingsForm = ({ initialValues, roleOptions, updateSettings, onCancel, on const onSubmit = form.handleSubmit((values) => mutation.mutate(values)); return ( -
+ - {({ ref, ...field }) => } + {({ ref, ...field }) => }