mirror of
https://github.com/tiennm99/litellm.git
synced 2026-08-02 12:21:10 +00:00
[Fix] Update UI tests for GuardrailViewer rewrite and AllModelsTab QueryClient
GuardrailViewer was rewritten from ant-design Collapse to a custom card layout. Tests now match the new component: updated header text, ms-based duration, expand-to-reveal provider details, and removed ant-collapse references. AllModelsTab tests failed because ModelSettingsModal now uses useMutation via useStoreModelInDB. Switched from bare render() to renderWithProviders() which wraps in QueryClientProvider. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
ed4b654934
commit
e5619c39a0
+9
-9
@@ -1,5 +1,5 @@
|
||||
import * as useAuthorizedModule from "@/app/(dashboard)/hooks/useAuthorized";
|
||||
import { render, screen, waitFor } from "@testing-library/react";
|
||||
import { renderWithProviders, screen, waitFor } from "../../../../../tests/test-utils";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import AllModelsTab from "./AllModelsTab";
|
||||
|
||||
@@ -116,7 +116,7 @@ describe("AllModelsTab", () => {
|
||||
|
||||
mockUseModelCostMap.mockReturnValueOnce(createModelCostMapMock({}));
|
||||
|
||||
render(<AllModelsTab {...defaultProps} />);
|
||||
renderWithProviders(<AllModelsTab {...defaultProps} />);
|
||||
expect(screen.getByText("Current Team:")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
@@ -172,7 +172,7 @@ describe("AllModelsTab", () => {
|
||||
|
||||
mockUseModelsInfo.mockReturnValue({ data: modelData, isLoading: false, error: null });
|
||||
|
||||
render(<AllModelsTab {...defaultProps} />);
|
||||
renderWithProviders(<AllModelsTab {...defaultProps} />);
|
||||
|
||||
// Component shows API total_count (2), not filtered count
|
||||
// Since default is "personal" team and models don't have direct_access, they're filtered out
|
||||
@@ -233,7 +233,7 @@ describe("AllModelsTab", () => {
|
||||
|
||||
mockUseModelsInfo.mockReturnValue({ data: modelData, isLoading: false, error: null });
|
||||
|
||||
render(<AllModelsTab {...defaultProps} />);
|
||||
renderWithProviders(<AllModelsTab {...defaultProps} />);
|
||||
|
||||
// Component shows API total_count (2), not filtered count
|
||||
// Since default is "personal" team and models don't have direct_access, they're filtered out
|
||||
@@ -280,7 +280,7 @@ describe("AllModelsTab", () => {
|
||||
|
||||
mockUseModelsInfo.mockReturnValue({ data: modelData, isLoading: false, error: null });
|
||||
|
||||
render(<AllModelsTab {...defaultProps} />);
|
||||
renderWithProviders(<AllModelsTab {...defaultProps} />);
|
||||
|
||||
// Component shows API total_count (2), but only 1 model has direct_access
|
||||
await waitFor(() => {
|
||||
@@ -338,7 +338,7 @@ describe("AllModelsTab", () => {
|
||||
|
||||
mockUseModelsInfo.mockReturnValue({ data: modelData, isLoading: false, error: null });
|
||||
|
||||
render(<AllModelsTab {...defaultProps} />);
|
||||
renderWithProviders(<AllModelsTab {...defaultProps} />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Config Model")).toBeInTheDocument();
|
||||
@@ -380,7 +380,7 @@ describe("AllModelsTab", () => {
|
||||
|
||||
mockUseModelsInfo.mockReturnValue({ data: modelData, isLoading: false, error: null });
|
||||
|
||||
render(<AllModelsTab {...defaultProps} />);
|
||||
renderWithProviders(<AllModelsTab {...defaultProps} />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Defined in config")).toBeInTheDocument();
|
||||
@@ -426,7 +426,7 @@ describe("AllModelsTab", () => {
|
||||
return { data: page1Data, isLoading: false, error: null };
|
||||
});
|
||||
|
||||
render(<AllModelsTab {...defaultProps} />);
|
||||
renderWithProviders(<AllModelsTab {...defaultProps} />);
|
||||
|
||||
await waitFor(() => {
|
||||
// Component calculates: ((1-1)*50)+1 = 1, Math.min(1*50, 2) = 2
|
||||
@@ -479,7 +479,7 @@ describe("AllModelsTab", () => {
|
||||
return { data: singlePageData, isLoading: false, error: null };
|
||||
});
|
||||
|
||||
render(<AllModelsTab {...defaultProps} />);
|
||||
renderWithProviders(<AllModelsTab {...defaultProps} />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Showing 1 - 1 of 1 results")).toBeInTheDocument();
|
||||
|
||||
+56
-43
@@ -19,68 +19,62 @@ describe("GuardrailViewer", () => {
|
||||
vi.resetModules();
|
||||
});
|
||||
|
||||
it("shows header, status pill color, duration rounding, and time labels", () => {
|
||||
it("shows header, status pill, and duration", () => {
|
||||
const data = makeGuardrailInformation({ duration: 1.23456, guardrail_status: "success" });
|
||||
renderWithProviders(<GuardrailViewer data={data} />);
|
||||
|
||||
expect(screen.getByText("Guardrail Information")).toBeInTheDocument();
|
||||
// header status pill (success => green)
|
||||
const statusBadges = screen.getAllByText("success");
|
||||
// there are two status locations: header chip and grid "Status"
|
||||
expect(statusBadges.length).toBeGreaterThanOrEqual(1);
|
||||
// Quick class assertion for at least one of them
|
||||
expect(statusBadges[0].className).toMatch(/bg-green-100/);
|
||||
expect(screen.getByText("Guardrails & Policy Compliance")).toBeInTheDocument();
|
||||
// header shows passed count
|
||||
expect(screen.getByText(/1 Passed/)).toBeInTheDocument();
|
||||
// The PASSED badge in the evaluation card
|
||||
expect(screen.getByText("PASSED")).toBeInTheDocument();
|
||||
|
||||
// duration displays with 4 decimals
|
||||
expect(screen.getByText(/1\.2346s/)).toBeInTheDocument();
|
||||
|
||||
// time labels exist
|
||||
expect(screen.getByText("Start Time:")).toBeInTheDocument();
|
||||
expect(screen.getByText("End Time:")).toBeInTheDocument();
|
||||
// duration displays in ms format: Math.round(1.23456 * 1000) = 1235
|
||||
expect(screen.getByText("1235ms")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("calculates and displays masked entity totals with pluralization", () => {
|
||||
it("calculates and displays masked entity totals", async () => {
|
||||
const user = userEvent.setup();
|
||||
const data = makeGuardrailInformation({
|
||||
masked_entity_count: { EMAIL_ADDRESS: 2, PHONE_NUMBER: 1 },
|
||||
});
|
||||
renderWithProviders(<GuardrailViewer data={data} />);
|
||||
|
||||
expect(screen.getByText("3 masked entities")).toBeInTheDocument();
|
||||
// summary chips for each entry
|
||||
// In collapsed state, the match count badge is visible
|
||||
expect(screen.getByText("3 matched")).toBeInTheDocument();
|
||||
|
||||
// Expand the evaluation card to see entity details
|
||||
await user.click(screen.getByText("pii-rail"));
|
||||
// summary chips for each entry inside expanded card
|
||||
expect(screen.getByText("EMAIL_ADDRESS: 2")).toBeInTheDocument();
|
||||
expect(screen.getByText("PHONE_NUMBER: 1")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("hides masked badge & summary when count is zero/empty", () => {
|
||||
it("hides matched badge when count is zero/empty", () => {
|
||||
const data = makeGuardrailInformation({ masked_entity_count: {} });
|
||||
renderWithProviders(<GuardrailViewer data={data} />);
|
||||
|
||||
expect(screen.queryByText(/masked entity/)).not.toBeInTheDocument();
|
||||
expect(screen.queryByText("Masked Entity Summary")).not.toBeInTheDocument();
|
||||
expect(screen.queryByText(/matched/)).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("toggles main section open/closed and chevron rotation class", async () => {
|
||||
it("toggles evaluation card open/closed on click", async () => {
|
||||
const user = userEvent.setup();
|
||||
const data = makeGuardrailInformation();
|
||||
const { container } = renderWithProviders(<GuardrailViewer data={data} />);
|
||||
|
||||
const header = screen.getByText("Guardrail Information").closest(".ant-collapse-header")!;
|
||||
// Initially expanded (content is visible)
|
||||
expect(screen.getByText("Masked Entity Summary")).toBeInTheDocument();
|
||||
|
||||
// Click to collapse
|
||||
await user.click(header);
|
||||
// Wait for collapse animation and content to be hidden
|
||||
await waitFor(() => {
|
||||
const contentBox = container.querySelector(".ant-collapse-content-box");
|
||||
expect(contentBox).not.toBeVisible();
|
||||
const data = makeGuardrailInformation({
|
||||
masked_entity_count: { EMAIL_ADDRESS: 2 },
|
||||
});
|
||||
renderWithProviders(<GuardrailViewer data={data} />);
|
||||
|
||||
// Click to expand again
|
||||
await user.click(header);
|
||||
// Wait for expand animation
|
||||
// Initially collapsed — masked entity details not visible
|
||||
expect(screen.queryByText("EMAIL_ADDRESS: 2")).not.toBeInTheDocument();
|
||||
|
||||
// Click to expand
|
||||
await user.click(screen.getByText("pii-rail"));
|
||||
expect(screen.getByText("EMAIL_ADDRESS: 2")).toBeInTheDocument();
|
||||
|
||||
// Click again to collapse
|
||||
await user.click(screen.getByText("pii-rail"));
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Masked Entity Summary")).toBeVisible();
|
||||
expect(screen.queryByText("EMAIL_ADDRESS: 2")).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -97,6 +91,9 @@ describe("GuardrailViewer", () => {
|
||||
});
|
||||
renderWithProviders(<Component data={data} />);
|
||||
|
||||
// Expand the card to see provider-specific content
|
||||
const user = userEvent.setup();
|
||||
await user.click(screen.getByText("pii-rail"));
|
||||
expect(screen.getByTestId("presidio-mock")).toHaveTextContent("presidio 2");
|
||||
});
|
||||
|
||||
@@ -112,6 +109,10 @@ describe("GuardrailViewer", () => {
|
||||
guardrail_response: [makeEntity()],
|
||||
});
|
||||
renderWithProviders(<Component data={data} />);
|
||||
|
||||
// Expand the card to see provider-specific content
|
||||
const user = userEvent.setup();
|
||||
await user.click(screen.getByText("pii-rail"));
|
||||
expect(screen.getByTestId("presidio-mock")).toHaveTextContent("count:1");
|
||||
});
|
||||
|
||||
@@ -127,22 +128,31 @@ describe("GuardrailViewer", () => {
|
||||
guardrail_response: makeBedrockResponse({ action: "GUARDRAIL_INTERVENED" }),
|
||||
});
|
||||
renderWithProviders(<Component data={data} />);
|
||||
|
||||
// Expand the card to see provider-specific content
|
||||
const user = userEvent.setup();
|
||||
await user.click(screen.getByText("pii-rail"));
|
||||
expect(screen.getByTestId("bedrock-mock")).toHaveTextContent("GUARDRAIL_INTERVENED");
|
||||
});
|
||||
|
||||
it("unknown provider renders neither Presidio nor Bedrock details", () => {
|
||||
it("unknown provider renders neither Presidio nor Bedrock details", async () => {
|
||||
const user = userEvent.setup();
|
||||
const data = makeGuardrailInformation({
|
||||
guardrail_provider: "unknown",
|
||||
});
|
||||
renderWithProviders(<GuardrailViewer data={data} />);
|
||||
// Summary still present
|
||||
expect(screen.getByText("Guardrail Information")).toBeInTheDocument();
|
||||
// No provider sections
|
||||
// Header still present
|
||||
expect(screen.getByText("Guardrails & Policy Compliance")).toBeInTheDocument();
|
||||
|
||||
// Expand the card
|
||||
await user.click(screen.getByText("pii-rail"));
|
||||
// No Presidio or Bedrock sections
|
||||
expect(screen.queryByText(/Detected Entities/)).not.toBeInTheDocument();
|
||||
expect(screen.queryByText(/Raw Bedrock Guardrail Response/)).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("integration: renders with real Bedrock details without mocks", () => {
|
||||
it("integration: renders with real Bedrock details without mocks", async () => {
|
||||
const user = userEvent.setup();
|
||||
const data = makeGuardrailInformation({
|
||||
guardrail_provider: "bedrock",
|
||||
guardrail_response: makeBedrockResponse({
|
||||
@@ -152,6 +162,9 @@ describe("GuardrailViewer", () => {
|
||||
});
|
||||
renderWithProviders(<GuardrailViewer data={data} />);
|
||||
|
||||
// Expand the card to reveal Bedrock details
|
||||
await user.click(screen.getByText("pii-rail"));
|
||||
|
||||
// Bedrock summary bits
|
||||
expect(screen.getByText("Outputs")).toBeInTheDocument();
|
||||
expect(screen.getByText("ok")).toBeInTheDocument();
|
||||
|
||||
Reference in New Issue
Block a user