fix: pre-release fixes for code formatting, i18n, and test suite

- Run cargo fmt to fix Rust code formatting (lib.rs)
- Add missing i18n keys: migration.success, agents.title (zh/en/ja)
- Replace hardcoded strings "Agents" and "MCP" with t() calls in App.tsx
- Fix test mocks and assertions:
  - Add providersApi.updateTrayMenu to useSettings.test.tsx mock
  - Update SettingsPage mock path in App.test.tsx
  - Fix toast message assertion in integration/SettingsDialog.test.tsx
  - Add autoSaveSettings to SettingsDialog component test mock
  - Fix loading state test to check spinner instead of title
  - Update import button name matching for selected file state
  - Fix save button test to switch to advanced tab first
  - Remove obsolete cancel button test (button no longer exists)

Test results improved from 99 passed / 17 failed to 104 passed / 11 failed
This commit is contained in:
Jason
2025-11-28 15:53:25 +08:00
parent 00f78e4546
commit 3878a16c4f
9 changed files with 46 additions and 31 deletions

View File

@@ -35,6 +35,7 @@ interface SettingsMock {
resetDirectory: ReturnType<typeof vi.fn>;
resetAppConfigDir: ReturnType<typeof vi.fn>;
saveSettings: ReturnType<typeof vi.fn>;
autoSaveSettings: ReturnType<typeof vi.fn>;
resetSettings: ReturnType<typeof vi.fn>;
acknowledgeRestart: ReturnType<typeof vi.fn>;
}
@@ -66,6 +67,7 @@ const createSettingsMock = (overrides: Partial<SettingsMock> = {}) => {
resetDirectory: vi.fn(),
resetAppConfigDir: vi.fn(),
saveSettings: vi.fn().mockResolvedValue({ requiresRestart: false }),
autoSaveSettings: vi.fn().mockResolvedValue({ requiresRestart: false }),
resetSettings: vi.fn(),
acknowledgeRestart: vi.fn(),
};
@@ -247,7 +249,8 @@ describe("SettingsPage Component", () => {
render(<SettingsPage open={true} onOpenChange={vi.fn()} />);
expect(screen.queryByText("language:zh")).not.toBeInTheDocument();
expect(screen.getByText("settings.title")).toBeInTheDocument();
// 加载状态下显示 spinner 而不是表单内容
expect(document.querySelector(".animate-spin")).toBeInTheDocument();
});
it("should reset import/export status when dialog transitions to open", () => {
@@ -264,6 +267,7 @@ describe("SettingsPage Component", () => {
it("should render general and advanced tabs and trigger child callbacks", () => {
const onOpenChange = vi.fn();
// 设置 selectedFile 后,按钮显示 settings.import可执行导入
importExportMock = createImportExportMock({
selectedFile: "/tmp/config.json",
});
@@ -284,21 +288,20 @@ describe("SettingsPage Component", () => {
});
fireEvent.click(screen.getByText("settings.tabAdvanced"));
fireEvent.click(
screen.getByRole("button", { name: "settings.selectConfigFile" }),
);
expect(importExportMock.selectImportFile).toHaveBeenCalled();
// 有文件时,点击导入按钮执行 importConfig
fireEvent.click(
screen.getByRole("button", { name: /settings\.import/ }),
);
expect(importExportMock.importConfig).toHaveBeenCalled();
fireEvent.click(
screen.getByRole("button", { name: "settings.exportConfig" }),
);
expect(importExportMock.exportConfig).toHaveBeenCalled();
fireEvent.click(screen.getByRole("button", { name: "settings.import" }));
expect(importExportMock.importConfig).toHaveBeenCalled();
fireEvent.click(screen.getByRole("button", { name: "common.clear" }));
// 清除选择按钮
fireEvent.click(screen.getByRole("button", { name: "Clear selection" }));
expect(importExportMock.clearSelection).toHaveBeenCalled();
});
@@ -330,7 +333,9 @@ describe("SettingsPage Component", () => {
render(<SettingsPage open={true} onOpenChange={onOpenChange} />);
fireEvent.click(screen.getByText("common.save"));
// 保存按钮在 advanced tab 中
fireEvent.click(screen.getByText("settings.tabAdvanced"));
fireEvent.click(screen.getByRole("button", { name: /common\.save/ }));
await waitFor(() => {
expect(settingsMock.saveSettings).toHaveBeenCalledTimes(1);
@@ -341,20 +346,6 @@ describe("SettingsPage Component", () => {
});
});
it("should reset settings and close dialog when clicking cancel", () => {
const onOpenChange = vi.fn();
render(<SettingsPage open={true} onOpenChange={onOpenChange} />);
fireEvent.click(screen.getByText("common.cancel"));
expect(settingsMock.resetSettings).toHaveBeenCalledTimes(1);
expect(settingsMock.acknowledgeRestart).toHaveBeenCalledTimes(1);
expect(importExportMock.clearSelection).toHaveBeenCalledTimes(1);
expect(importExportMock.resetStatus).toHaveBeenCalledTimes(2);
expect(onOpenChange).toHaveBeenCalledWith(false);
});
it("should show restart prompt and allow immediate restart after save", async () => {
settingsMock = createSettingsMock({
requiresRestart: true,

View File

@@ -8,6 +8,7 @@ const useSettingsQueryMock = vi.fn();
const setAppConfigDirOverrideMock = vi.fn();
const applyClaudePluginConfigMock = vi.fn();
const syncCurrentProvidersLiveMock = vi.fn();
const updateTrayMenuMock = vi.fn();
const toastErrorMock = vi.fn();
const toastSuccessMock = vi.fn();
@@ -52,6 +53,9 @@ vi.mock("@/lib/api", () => ({
syncCurrentProvidersLive: (...args: unknown[]) =>
syncCurrentProvidersLiveMock(...args),
},
providersApi: {
updateTrayMenu: (...args: unknown[]) => updateTrayMenuMock(...args),
},
}));
const createSettingsFormMock = (overrides: Record<string, unknown> = {}) => ({

View File

@@ -109,8 +109,8 @@ vi.mock("@/components/ConfirmDialog", () => ({
) : null,
}));
vi.mock("@/components/settings/SettingsDialog", () => ({
SettingsDialog: ({ open, onOpenChange, onImportSuccess }: any) =>
vi.mock("@/components/settings/SettingsPage", () => ({
SettingsPage: ({ open, onOpenChange, onImportSuccess }: any) =>
open ? (
<div data-testid="settings-dialog">
<button onClick={() => onImportSuccess?.()}>

View File

@@ -264,7 +264,7 @@ describe("SettingsPage integration", () => {
await waitFor(() => expect(toastErrorMock).toHaveBeenCalled());
const cancelMessage = toastErrorMock.mock.calls.at(-1)?.[0] as string;
expect(cancelMessage).toMatch(
/settings\.selectFileFailed|选择保存位置失败/,
/settings\.selectFileFailed|选择.*保存路径/,
);
toastErrorMock.mockClear();