Files
cc-switch/tests/components/ManagedAuthStatusError.test.tsx
Jason 0b1af534fa feat(ui): apply the v7 design review
Give each visual signal one meaning across pages.

Modes: only the mode being viewed gets the raised fill; the active mode
keeps its colour and dot. The Stack preview starts Stack from a row
("Start Stack with this as default…"), like the routing preview, so the
dialog no longer picks a default. The mode help moves next to the tabs,
the routing status drops the local address, the two-step explanation and
the "advanced feature" checkbox leave the dialogs, and added cards in
additive apps are white; grey now only marks the default provider.

Navigation: page-level switches use underline tabs (new PageTabs, also
used by Usage), with a smaller second level for OpenClaw config, Hermes
memory and Skills. Segmented controls stay for modes and in-page filters.
Hermes memory keeps unsaved edits when switching files.

Danger: irreversible confirmations use the red button; recoverable ones
(back to direct, remove from app, uninstall Skills, rebuild Codex usage,
delete env vars) no longer use red.

Pages:
- MCP/Skills matrix: one square checkbox shape for every state, and the
  column header highlights and shows the app name on hover or focus.
- Skills: drop the update banner; "N updatable" in the header filters
  the table and offers "Update all"; the unmanaged banner keeps only
  Ignore; the installed count lives on the tab only.
- Add provider: "All" is split by category, names sorted within each;
  versions split into plan and region, shown as two segmented rows when
  they form a grid and as a dropdown otherwise.
- Usage: the trend chart shows the selected metric only; today's log
  rows show the time only and the provider column gets the space.
- Sessions: the reader merges into the page header.
- Accounts: rows say how many providers use them, and removing an
  account confirms first and names the affected providers.
- Apps: small neutral switches for "Show"; Settings says "Check for new
  versions of your apps at launch".

Also add the provider empty state, a one-time "new layout" notice for
existing users, and a 1040px max width for card lists on wide windows.
2026-10-03 23:28:58 +08:00

92 lines
2.9 KiB
TypeScript

import { fireEvent, render, screen } from "@testing-library/react";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { CodexOAuthSection } from "@/components/providers/forms/CodexOAuthSection";
import { CopilotAuthSection } from "@/components/providers/forms/CopilotAuthSection";
const authMocks = vi.hoisted(() => ({
refetchCodex: vi.fn(),
refetchCopilot: vi.fn(),
}));
const failedStatus = (refetchStatus: () => void) => ({
accounts: [],
defaultAccountId: null,
migrationError: null,
isStatusSuccess: false,
isStatusError: true,
hasAnyAccount: false,
pollingState: "idle" as const,
deviceCode: null,
error: null,
isPolling: false,
isAddingAccount: false,
isRemovingAccount: false,
isSettingDefaultAccount: false,
addAccount: vi.fn(),
removeAccount: vi.fn(),
setDefaultAccount: vi.fn(),
cancelAuth: vi.fn(),
logout: vi.fn(),
refetchStatus,
});
vi.mock("@/components/providers/forms/hooks/useCodexOauth", () => ({
useCodexOauth: () => failedStatus(authMocks.refetchCodex),
}));
vi.mock("@/components/providers/forms/hooks/useCopilotAuth", () => ({
useCopilotAuth: () => failedStatus(authMocks.refetchCopilot),
}));
// 「N 个供应商在用」要读供应商列表(React Query);这里不关心,给空
vi.mock("@/components/providers/forms/hooks/useManagedAccountUsers", () => ({
useManagedAccountUsers: () => () => [],
}));
describe("managed auth status failures", () => {
beforeEach(() => {
authMocks.refetchCodex.mockResolvedValue(undefined);
authMocks.refetchCopilot.mockResolvedValue(undefined);
});
it("shows a retryable error instead of an empty Codex account selector", () => {
const onAccountSelect = vi.fn();
render(
<CodexOAuthSection
mode="select"
selectedAccountId="acct-existing"
onAccountSelect={onAccountSelect}
/>,
);
expect(screen.getByRole("alert")).toHaveTextContent(
"无法加载 ChatGPT 账号状态,请重试。",
);
expect(screen.queryByRole("combobox")).not.toBeInTheDocument();
expect(onAccountSelect).not.toHaveBeenCalled();
fireEvent.click(screen.getByRole("button", { name: "重试" }));
expect(authMocks.refetchCodex).toHaveBeenCalledTimes(1);
});
it("shows a retryable error instead of an empty Copilot account selector", () => {
const onAccountSelect = vi.fn();
render(
<CopilotAuthSection
mode="select"
selectedAccountId="acct-existing"
onAccountSelect={onAccountSelect}
/>,
);
expect(screen.getByRole("alert")).toHaveTextContent(
"无法加载 GitHub Copilot 账号状态,请重试。",
);
expect(screen.queryByRole("combobox")).not.toBeInTheDocument();
expect(onAccountSelect).not.toHaveBeenCalled();
fireEvent.click(screen.getByRole("button", { name: "重试" }));
expect(authMocks.refetchCopilot).toHaveBeenCalledTimes(1);
});
});