mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-07 02:59:05 +00:00
fix(ui): keep confirmation modal open on API failure, restore response test
executeSave now returns a boolean; handleConfirmSave only clears modal state on success. Adds tests for the error path and for verifying the UI reflects API response values after a successful save.
This commit is contained in:
parent
30181aa71c
commit
94c22707a2
2 changed files with 92 additions and 8 deletions
|
|
@ -310,6 +310,86 @@ describe("DefaultUserSettings", () => {
|
|||
// Should still be in edit mode (Save Changes button visible)
|
||||
expect(screen.getByText("Save Changes")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should keep modal open when API save fails", async () => {
|
||||
const settingsWithTeams = {
|
||||
...mockSettings,
|
||||
values: {
|
||||
...mockSettings.values,
|
||||
teams: [
|
||||
{ team_id: "team-alpha", max_budget_in_team: 50, user_role: "user" },
|
||||
],
|
||||
},
|
||||
};
|
||||
mockGetInternalUserSettings.mockResolvedValue(settingsWithTeams);
|
||||
mockUpdateInternalUserSettings.mockRejectedValue(new Error("Server error"));
|
||||
|
||||
render(<DefaultUserSettings {...defaultProps} />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Edit Settings")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
// Enter edit mode, remove team, click Save → modal opens
|
||||
act(() => {
|
||||
fireEvent.click(screen.getByText("Edit Settings"));
|
||||
});
|
||||
act(() => {
|
||||
fireEvent.click(screen.getAllByText("Remove")[0]);
|
||||
});
|
||||
act(() => {
|
||||
fireEvent.click(screen.getByText("Save Changes"));
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Review Changes")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
// Click Confirm — API will reject
|
||||
await act(async () => {
|
||||
fireEvent.click(screen.getByText("Confirm Changes"));
|
||||
});
|
||||
|
||||
// Modal should still be open (not cleared on failure)
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Review Changes")).toBeInTheDocument();
|
||||
expect(screen.getByText("Confirm Changes")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it("should reflect API response values after successful save", async () => {
|
||||
mockGetInternalUserSettings.mockResolvedValue(mockSettings);
|
||||
// API returns different max_budget than what was sent
|
||||
mockUpdateInternalUserSettings.mockResolvedValue({
|
||||
settings: {
|
||||
...mockSettings.values,
|
||||
max_budget: 2000,
|
||||
},
|
||||
});
|
||||
|
||||
render(<DefaultUserSettings {...defaultProps} />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Edit Settings")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
act(() => {
|
||||
fireEvent.click(screen.getByText("Edit Settings"));
|
||||
});
|
||||
|
||||
const saveButton = screen.getByText("Save Changes");
|
||||
act(() => {
|
||||
fireEvent.click(saveButton);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockUpdateInternalUserSettings).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
// Should exit edit mode and show the API response value
|
||||
expect(screen.getByText("Edit Settings")).toBeInTheDocument();
|
||||
expect(screen.getByText("2000")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -167,18 +167,20 @@ const DefaultUserSettings: React.FC<DefaultUserSettingsProps> = ({
|
|||
fetchSSOSettings();
|
||||
}, [accessToken]);
|
||||
|
||||
/** Perform the actual API save with the given processed values. */
|
||||
const executeSave = async (processedValues: DefaultUserSettingsValues) => {
|
||||
if (!accessToken) return;
|
||||
/** Perform the actual API save. Returns true on success, false on failure. */
|
||||
const executeSave = async (processedValues: DefaultUserSettingsValues): Promise<boolean> => {
|
||||
if (!accessToken) return false;
|
||||
|
||||
setSaving(true);
|
||||
try {
|
||||
const updatedSettings = await updateInternalUserSettings(accessToken, processedValues);
|
||||
setSettings({ ...settings, values: updatedSettings.settings });
|
||||
setIsEditing(false);
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error("Error updating SSO settings:", error);
|
||||
NotificationManager.fromBackend("Failed to update settings: " + error);
|
||||
return false;
|
||||
} finally {
|
||||
setSaving(false);
|
||||
}
|
||||
|
|
@ -209,13 +211,15 @@ const DefaultUserSettings: React.FC<DefaultUserSettingsProps> = ({
|
|||
await executeSave(processedValues);
|
||||
};
|
||||
|
||||
/** Called when user confirms changes in the modal. */
|
||||
/** Called when user confirms changes in the modal. Only clears modal on success. */
|
||||
const handleConfirmSave = async () => {
|
||||
if (!pendingProcessedValues) return;
|
||||
await executeSave(pendingProcessedValues);
|
||||
setShowConfirmModal(false);
|
||||
setPendingChanges([]);
|
||||
setPendingProcessedValues(null);
|
||||
const success = await executeSave(pendingProcessedValues);
|
||||
if (success) {
|
||||
setShowConfirmModal(false);
|
||||
setPendingChanges([]);
|
||||
setPendingProcessedValues(null);
|
||||
}
|
||||
};
|
||||
|
||||
const handleTextInputChange = (key: keyof DefaultUserSettingsValues, value: DefaultUserSettingsValues[typeof key]) => {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue