mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-10-26 01:34:51 +08:00
fix(ui): stop expanding stale cached usage tiers on ineligible provider cards
A disabled React Query observer still returns the existing cache entry for the same key, so a provider switched to the official subscription template (or with its usage script disabled) kept showing the multi-plan chevron and the expanded tiers left over from a previous script result. Gate hasMultiplePlans with the same predicate that enables the usage query, extracted as scriptUsageActive so the two cannot drift apart again.
This commit is contained in:
1 parent
8272707d5e
commit
2c735bd9de
2 files changed
+117
-2
No files matched your search
@@ -317,15 +317,24 @@ export function ProviderCard({
|
||||
? provider.meta?.usage_script?.autoQueryInterval || 0
|
||||
: 0;
|
||||
|
||||
// 脚本用量只在「已启用 + 非官方 + 非官方订阅模板」时才查询;展开判定必须复用同一谓词,
|
||||
// 因为禁用的 React Query observer 仍会返回同 key 的旧缓存。
|
||||
const scriptUsageActive =
|
||||
usageEnabled && !isOfficial && !isOfficialSubscriptionUsage;
|
||||
const { data: usage } = useUsageQuery(provider.id, appId, {
|
||||
enabled: usageEnabled && !isOfficial && !isOfficialSubscriptionUsage,
|
||||
enabled: scriptUsageActive,
|
||||
autoQueryInterval,
|
||||
});
|
||||
|
||||
const isTokenPlan =
|
||||
provider.meta?.usage_script?.templateType === "token_plan";
|
||||
// 官方订阅的额度窗口不能按普通多套餐展开;缓存残留的旧脚本结果同样不认。
|
||||
const hasMultiplePlans =
|
||||
usage?.success && usage.data && usage.data.length > 1 && !isTokenPlan;
|
||||
scriptUsageActive &&
|
||||
!isTokenPlan &&
|
||||
usage?.success &&
|
||||
usage.data &&
|
||||
usage.data.length > 1;
|
||||
|
||||
const [isExpanded, setIsExpanded] = useState(false);
|
||||
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
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}
|
||||
isProxyRunning={false}
|
||||
onSwitch={vi.fn()}
|
||||
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.queryByTitle("收起")).not.toBeInTheDocument();
|
||||
expect(screen.queryByTitle("展开")).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.queryByTitle("收起")).not.toBeInTheDocument();
|
||||
expect(screen.queryByTitle("展开")).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.getByTitle("收起"));
|
||||
expect(screen.queryByText("expanded-plan-details")).not.toBeInTheDocument();
|
||||
expect(screen.getByTitle("展开")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user