diff --git a/ui/litellm-dashboard/src/components/view_logs/index.test.tsx b/ui/litellm-dashboard/src/components/view_logs/index.test.tsx index a44a8ada43d..a19e772e340 100644 --- a/ui/litellm-dashboard/src/components/view_logs/index.test.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/index.test.tsx @@ -1,8 +1,45 @@ -import { render, screen } from "@testing-library/react"; -import { describe, expect, it } from "vitest"; -import { RequestViewer } from "./index"; +import { render, screen, waitFor } from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; +import { beforeEach, describe, expect, it, vi } from "vitest"; +import SpendLogsTable, { RequestViewer } from "./index"; import type { LogEntry } from "./columns"; import type { Row } from "@tanstack/react-table"; +import type { Team } from "../key_team_helpers/key_list"; +import { renderWithProviders } from "../../../tests/test-utils"; + +const mockHandleFilterResetFromHook = vi.fn(); +vi.mock("./log_filter_logic", () => ({ + useLogFilterLogic: vi.fn(() => ({ + filters: {}, + filteredLogs: { data: [], total: 0, page: 1, page_size: 50, total_pages: 1 }, + allTeams: [], + allKeyAliases: [], + handleFilterChange: vi.fn(), + handleFilterReset: mockHandleFilterResetFromHook, + })), +})); + +vi.mock("../networking", async (importOriginal) => { + const actual = await importOriginal(); + return { + ...actual, + uiSpendLogsCall: vi.fn().mockResolvedValue({ + data: [], + total: 0, + page: 1, + page_size: 50, + total_pages: 0, + }), + keyListCall: vi.fn().mockResolvedValue({ keys: [] }), + keyInfoV1Call: vi.fn().mockResolvedValue({ info: {} }), + allEndUsersCall: vi.fn().mockResolvedValue([]), + }; +}); + +vi.mock("../key_team_helpers/filter_helpers", () => ({ + fetchAllKeyAliases: vi.fn().mockResolvedValue([]), + fetchAllTeams: vi.fn().mockResolvedValue([]), +})); const baseLogEntry: LogEntry = { request_id: "chatcmpl-test-id", @@ -87,3 +124,63 @@ describe("Request Viewer", () => { expect(screen.queryByText("LiteLLM Overhead:")).not.toBeInTheDocument(); }); }); + +describe("SpendLogsTable", () => { + const defaultProps = { + accessToken: "test-token", + token: "test-token", + userRole: "Admin", + userID: "user-1", + allTeams: [] as Team[], + premiumUser: false, + }; + + beforeEach(() => { + vi.clearAllMocks(); + // Clear sessionStorage to avoid isLiveTail state from previous tests + sessionStorage.clear(); + }); + + it("should call handleFilterResetFromHook when Reset Filters is clicked", async () => { + const user = userEvent.setup(); + renderWithProviders(); + + const resetButton = screen.getByRole("button", { name: "Reset Filters" }); + await user.click(resetButton); + + await waitFor(() => { + expect(mockHandleFilterResetFromHook).toHaveBeenCalledTimes(1); + }); + }); + + it("should reset custom date range to default when Reset Filters is clicked", async () => { + const user = userEvent.setup(); + renderWithProviders(); + + // Open the time range quick select dropdown (button shows current range like "Last 24 Hours") + const quickSelectButton = screen.getByRole("button", { name: /Last 24 Hours|Last 15 Minutes|Last Hour|Last 4 Hours|Last 7 Days/i }); + await user.click(quickSelectButton); + + // Click "Custom Range" to enable custom date selection + const customRangeButton = await screen.findByRole("button", { name: "Custom Range" }); + await user.click(customRangeButton); + + // Custom date inputs should now be visible (start and end datetime-local inputs) + const datetimeInputs = document.querySelectorAll('input[type="datetime-local"]'); + expect(datetimeInputs.length).toBeGreaterThanOrEqual(2); + + // Click Reset Filters - this should reset the custom date range and hide custom inputs + const resetButton = screen.getByRole("button", { name: "Reset Filters" }); + await user.click(resetButton); + + await waitFor(() => { + expect(mockHandleFilterResetFromHook).toHaveBeenCalled(); + }); + + // After reset, custom date inputs should be hidden (isCustomDate reset to false) + await waitFor(() => { + const inputsAfterReset = document.querySelectorAll('input[type="datetime-local"]'); + expect(inputsAfterReset.length).toBe(0); + }); + }); +}); diff --git a/ui/litellm-dashboard/src/components/view_logs/index.tsx b/ui/litellm-dashboard/src/components/view_logs/index.tsx index 89a00fbb4cd..5ad47c235e3 100644 --- a/ui/litellm-dashboard/src/components/view_logs/index.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/index.tsx @@ -239,7 +239,7 @@ export default function SpendLogsTable({ allTeams: hookAllTeams, allKeyAliases, handleFilterChange, - handleFilterReset, + handleFilterReset: handleFilterResetFromHook, } = useLogFilterLogic({ logs: logsData, accessToken, @@ -274,6 +274,16 @@ export default function SpendLogsTable({ [accessToken, currentPage, pageSize], ); + const handleFilterReset = useCallback(() => { + handleFilterResetFromHook(); + // Reset custom time range to default (last 24 hours) + setStartTime(moment().subtract(24, "hours").format("YYYY-MM-DDTHH:mm")); + setEndTime(moment().format("YYYY-MM-DDTHH:mm")); + setIsCustomDate(false); + setSelectedTimeInterval({ value: 24, unit: "hours" }); + setCurrentPage(1); + }, [handleFilterResetFromHook]); + // Add this effect to update selected filters when filter changes useEffect(() => { if (!accessToken) return;