test(ui): mock useCan so cost-optimization tests don't need a QueryClientProvider

The recent capability-gating pass added useCan calls to UsageTab.tsx,
CostOptimizationView.tsx, and the three new admin-only page.tsx files.
useCan reaches useIsOrgAdmin -> useOrganizations -> useQuery, so every
render now needs a QueryClientProvider or the tree throws "No QueryClient
set" and the /organization/list fetch is a legitimate side effect.

Mock useCan in the three unit tests, wired to the real hasCapability so
per-role behaviour still runs end-to-end. Narrow the three page
integration tests' loose "fetchMock was never called" assertion to the
specific admin-only endpoint each page owns; the org fetch was tripping
it after the capability check landed.

Co-authored-by: Krrish Dholakia <krrish-berri-2@users.noreply.github.com>
This commit is contained in:
Cursor Agent 2026-08-11 01:29:14 +00:00
parent 1d3b64c66f
commit bd2d334c10
No known key found for this signature in database
6 changed files with 29 additions and 8 deletions

View file

@ -1,6 +1,10 @@
import { fireEvent, render, waitFor } from "@testing-library/react";
import { describe, expect, it, vi } from "vitest";
vi.mock("@/app/(dashboard)/hooks/useCan", () => ({
default: () => true,
}));
const mockUserDailyActivityCall = vi.fn();
vi.mock("@/components/networking", () => ({

View file

@ -1,12 +1,20 @@
import { fireEvent, render } from "@testing-library/react";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { hasCapability, type Capability } from "@/utils/capabilities";
const { useAuthorizedMock } = vi.hoisted(() => ({ useAuthorizedMock: vi.fn() }));
const { useAuthorizedMock, useCanMock } = vi.hoisted(() => ({
useAuthorizedMock: vi.fn(),
useCanMock: vi.fn(),
}));
vi.mock("@/app/(dashboard)/hooks/useAuthorized", () => ({
default: useAuthorizedMock,
}));
vi.mock("@/app/(dashboard)/hooks/useCan", () => ({
default: useCanMock,
}));
vi.mock("./UsageTab", () => ({ __esModule: true, default: () => <div data-testid="usage-tab" /> }));
vi.mock("./PromptCompressionTab", () => ({ __esModule: true, default: () => <div data-testid="compression-tab" /> }));
vi.mock("./PromptCachingTab", () => ({ __esModule: true, default: () => <div data-testid="caching-tab" /> }));
@ -19,12 +27,14 @@ import CostOptimizationView from "./CostOptimizationView";
const renderView = (userRole = "Admin") => {
useAuthorizedMock.mockReturnValue({ accessToken: "test-token", userId: "u1", userRole });
useCanMock.mockImplementation((capability: Capability) => hasCapability(userRole, capability));
return render(<CostOptimizationView accessToken="test-token" userId="u1" userRole={userRole} />);
};
describe("CostOptimizationView", () => {
beforeEach(() => {
useAuthorizedMock.mockReturnValue({ accessToken: "test-token", userId: "u1", userRole: "Admin" });
useCanMock.mockImplementation((capability: Capability) => hasCapability("Admin", capability));
});
it("renders the four cost-optimization tabs", () => {

View file

@ -5,14 +5,23 @@ import type { ToolSpendResponse } from "@/components/networking";
import type { DailyData, SpendMetrics } from "@/components/UsagePage/types";
import { hasCapability, type Capability } from "@/utils/capabilities";
const mockGetToolSpend = vi.fn();
const { useAuthorizedMock } = vi.hoisted(() => ({ useAuthorizedMock: vi.fn() }));
const { useAuthorizedMock, useCanMock } = vi.hoisted(() => ({
useAuthorizedMock: vi.fn(),
useCanMock: vi.fn(),
}));
vi.mock("@/app/(dashboard)/hooks/useAuthorized", () => ({
default: useAuthorizedMock,
}));
vi.mock("@/app/(dashboard)/hooks/useCan", () => ({
default: useCanMock,
}));
vi.mock("@/components/networking", () => ({
getToolSpend: (...args: unknown[]) => mockGetToolSpend(...args),
}));
@ -106,6 +115,7 @@ const renderWith = (results: DailyData[], options: RenderOptions = {}) => {
} = options;
mockGetToolSpend.mockResolvedValue(toolSpend);
useAuthorizedMock.mockReturnValue({ accessToken: "test-token", userId: "u1", userRole });
useCanMock.mockImplementation((capability: Capability) => hasCapability(userRole, capability));
return render(
<UsageTab
accessToken="test-token"

View file

@ -46,8 +46,7 @@ describe("Guardrails Monitor page access by role", () => {
renderAs(userRole);
expect(await screen.findByText("Guardrails Monitor is only available to admin users.")).toBeInTheDocument();
await waitFor(() => expect(fetchMock).not.toHaveBeenCalled());
expect(requestedUrls().filter((url) => url.includes("/guardrails/usage"))).toEqual([]);
await waitFor(() => expect(requestedUrls().filter((url) => url.includes("/guardrails/usage"))).toEqual([]));
},
);
});

View file

@ -46,8 +46,7 @@ describe("Memory page access by role", () => {
renderAs(userRole);
expect(await screen.findByText("Memory is only available to admin users.")).toBeInTheDocument();
await waitFor(() => expect(fetchMock).not.toHaveBeenCalled());
expect(requestedUrls().filter((url) => url.includes("/v1/memory"))).toEqual([]);
await waitFor(() => expect(requestedUrls().filter((url) => url.includes("/v1/memory"))).toEqual([]));
},
);

View file

@ -46,8 +46,7 @@ describe("Workflows page access by role", () => {
renderAs(userRole);
expect(await screen.findByText("Workflow Runs is only available to admin users.")).toBeInTheDocument();
await waitFor(() => expect(fetchMock).not.toHaveBeenCalled());
expect(requestedUrls().filter((url) => url.includes("/v1/workflows"))).toEqual([]);
await waitFor(() => expect(requestedUrls().filter((url) => url.includes("/v1/workflows"))).toEqual([]));
},
);