Files
cc-switch/tests/components/ProviderCard.usageExpansion.test.tsx
千羽and千羽 57b1eff1d4 feat(ui): v8 — theme color, unified controls, motion, virtual lists (#7816)
Unify the visual and interaction rules on top of the v7 redesign.

- Theme color #F97315 for --action-* and --ring; dark mode uses #EA580C
  with white text, toned-down mode colors and translucent soft fills
- Buttons mapped to three sizes (28 / 32 / 36); inputs, selects and
  search fields share fieldClass; self-drawn checkbox and radio
- HoverTip replaces title on about 100 icon buttons; it stays closed
  when a closing menu returns focus to its trigger
- Install tailwindcss-animate so menu, popover, dialog and tooltip
  enter/exit animations take effect; sliding segment thumb and tab
  underline
- Remove the command palette and the header prompt entry; provider card
  primary actions become icon buttons
- Virtualize the session list and the Skills discovery list; optimistic
  toggles for MCP and Skills with a 300ms delayed disabled state
- Toasts drawn unstyled with status-colored icons and de-duplicated
- Windows: hide the system title bar and use in-app window controls;
  macOS: brand row under the traffic lights
- Focus rings only for keyboard users
- Skills discovery and the prompt list show the last result from a
  local cache and refresh in the background
- Drop the trailing ellipsis from 36 dialog-opening labels
- Provider list fills the main area

Co-authored-by: 千羽 <qyinter@users.noreply.github.com>
2026-10-03 23:28:59 +08:00

114 lines
3.9 KiB
TypeScript

import { QueryClientProvider } from "@tanstack/react-query";
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { describe, expect, it, vi } from "vitest";
import { ProviderCard } from "@/components/providers/ProviderCard";
import type { TemplateType } from "@/config/constants";
import { usageKeys } from "@/lib/query/usage";
import type { Provider } from "@/types";
import { createTestQueryClient } from "../utils/testQueryClient";
vi.mock("@/components/providers/ProviderActions", () => ({
ProviderActions: () => null,
}));
vi.mock("@/components/ProviderIcon", () => ({ ProviderIcon: () => null }));
vi.mock("@/components/UsageFooter", () => ({
default: ({ inline }: { inline: boolean }) =>
inline ? null : <div>expanded-plan-details</div>,
}));
vi.mock("@/components/SubscriptionQuotaFooter", () => ({
default: () => <div>official-subscription-quota</div>,
}));
vi.mock("@/lib/query/failover", () => ({
useProviderHealth: () => ({ data: undefined }),
}));
function renderCard({
official = false,
templateType = "custom",
enabled = true,
}: {
official?: boolean;
templateType?: TemplateType;
enabled?: boolean;
} = {}) {
const provider: Provider = {
id: "cached-usage-provider",
name: "Usage provider",
category: official ? "official" : "custom",
settingsConfig: {
env: { ANTHROPIC_BASE_URL: "https://example.com" },
},
meta: {
usage_script: { enabled, language: "javascript", code: "", templateType },
},
};
const queryClient = createTestQueryClient();
// A disabled React Query observer still receives existing cache entries.
queryClient.setQueryData(usageKeys.script(provider.id, "claude"), {
success: true,
data: [
{ planName: "five_hour", total: 100, used: 0, remaining: 100, unit: "%" },
{ planName: "seven_day", total: 100, used: 30, remaining: 70, unit: "%" },
],
});
return render(
<QueryClientProvider client={queryClient}>
<ProviderCard
provider={provider}
appId="claude"
isCurrent={true}
presentation={{ chips: [], buttons: [] }}
onEdit={vi.fn()}
onDelete={vi.fn()}
onConfigureUsage={vi.fn()}
onOpenWebsite={vi.fn()}
onDuplicate={vi.fn()}
/>
</QueryClientProvider>,
);
}
describe("ProviderCard cached usage expansion", () => {
it("keeps official subscription quota inline despite cached script tiers", () => {
renderCard({ official: true, templateType: "official_subscription" });
expect(screen.getByText("official-subscription-quota")).toBeInTheDocument();
expect(screen.queryByText("expanded-plan-details")).not.toBeInTheDocument();
expect(
screen.queryByRole("button", { name: "收起" }),
).not.toBeInTheDocument();
expect(
screen.queryByRole("button", { name: "展开" }),
).not.toBeInTheDocument();
});
it.each<Parameters<typeof renderCard>[0]>([
{ official: true },
{ templateType: "official_subscription" },
{ templateType: "token_plan" },
{ enabled: false },
])("does not expand ineligible cached usage: %j", (options) => {
renderCard(options);
expect(screen.queryByText("expanded-plan-details")).not.toBeInTheDocument();
expect(
screen.queryByRole("button", { name: "收起" }),
).not.toBeInTheDocument();
expect(
screen.queryByRole("button", { name: "展开" }),
).not.toBeInTheDocument();
});
it("still expands ordinary multi-plan usage and allows collapsing it", async () => {
const user = userEvent.setup();
renderCard();
expect(screen.getByText("expanded-plan-details")).toBeInTheDocument();
await user.click(screen.getByRole("button", { name: "收起" }));
expect(screen.queryByText("expanded-plan-details")).not.toBeInTheDocument();
expect(screen.getByRole("button", { name: "展开" })).toBeInTheDocument();
});
});