1
0

refactor: 迁移前端 antd 组件至 v6 规范 API,消除废弃用法

- 迁移静态 message 到 App.useApp() 模式,使 message 感知 ConfigProvider 上下文
- Button type/danger 迁移为 variant/color 新 API
- Space direction 迁移为 vertical 布尔属性
- Select.Option 子组件迁移为 options 属性
- AppLayout 硬编码颜色替换为 antd design token
- 优化 useThemeConfig:default/dark 改为静态导出,减少不必要的 hook 调用
- 同步更新 openspec 主规范文档
This commit is contained in:
2026-04-17 00:59:36 +08:00
parent 49818ed4d8
commit 6eeb38c15e
19 changed files with 121 additions and 96 deletions

View File

@@ -70,7 +70,7 @@ describe('AppLayout', () => {
const { container } = renderWithRouter(<AppLayout />);
const header = container.querySelector('.ant-layout-header') as HTMLElement;
expect(header.style.background).toBe('#141414');
expect(header.style.borderBottom).toContain('1px solid');
});
it('uses light menu theme in default mode', async () => {

View File

@@ -101,7 +101,7 @@ describe('ProviderTable', () => {
const onEdit = vi.fn();
render(<ProviderTable {...defaultProps} onEdit={onEdit} />);
const editButtons = screen.getAllByRole('button', { name: '编辑' });
const editButtons = screen.getAllByRole('button', { name: /编 ?辑/ });
await user.click(editButtons[0]);
expect(onEdit).toHaveBeenCalledTimes(1);

View File

@@ -6,15 +6,21 @@ import { setupServer } from 'msw/node';
import { useModels, useCreateModel, useUpdateModel, useDeleteModel } from '@/hooks/useModels';
import type { Model, CreateModelInput, UpdateModelInput } from '@/types';
// Mock antd message since it uses DOM APIs not available in jsdom
vi.mock('antd', () => ({
message: {
success: vi.fn(),
error: vi.fn(),
},
}));
const mockMessage = {
success: vi.fn(),
error: vi.fn(),
};
import { message } from 'antd';
vi.mock('antd', async (importOriginal) => {
const original = await importOriginal<typeof import('antd')>();
return {
...original,
App: {
...original.App,
useApp: () => ({ message: mockMessage }),
},
};
});
// Test data
const mockModels: Model[] = [
@@ -167,7 +173,7 @@ describe('useCreateModel', () => {
modelName: 'gpt-4.1',
});
expect(invalidateSpy).toHaveBeenCalledWith({ queryKey: ['models'] });
expect(message.success).toHaveBeenCalledWith('模型创建成功');
expect(mockMessage.success).toHaveBeenCalledWith('模型创建成功');
});
it('calls message.error on failure', async () => {
@@ -191,7 +197,7 @@ describe('useCreateModel', () => {
result.current.mutate(input);
await waitFor(() => expect(result.current.isError).toBe(true));
expect(message.error).toHaveBeenCalled();
expect(mockMessage.error).toHaveBeenCalled();
});
});
@@ -222,7 +228,7 @@ describe('useUpdateModel', () => {
modelName: 'gpt-4o-updated',
});
expect(invalidateSpy).toHaveBeenCalledWith({ queryKey: ['models'] });
expect(message.success).toHaveBeenCalledWith('模型更新成功');
expect(mockMessage.success).toHaveBeenCalledWith('模型更新成功');
});
it('calls message.error on failure', async () => {
@@ -239,7 +245,7 @@ describe('useUpdateModel', () => {
result.current.mutate({ id: 'model-1', input: { modelName: 'Updated' } });
await waitFor(() => expect(result.current.isError).toBe(true));
expect(message.error).toHaveBeenCalled();
expect(mockMessage.error).toHaveBeenCalled();
});
});
@@ -265,7 +271,7 @@ describe('useDeleteModel', () => {
await waitFor(() => expect(result.current.isSuccess).toBe(true));
expect(invalidateSpy).toHaveBeenCalledWith({ queryKey: ['models'] });
expect(message.success).toHaveBeenCalledWith('模型删除成功');
expect(mockMessage.success).toHaveBeenCalledWith('模型删除成功');
});
it('calls message.error on failure', async () => {
@@ -282,6 +288,6 @@ describe('useDeleteModel', () => {
result.current.mutate('model-1');
await waitFor(() => expect(result.current.isError).toBe(true));
expect(message.error).toHaveBeenCalled();
expect(mockMessage.error).toHaveBeenCalled();
});
});

View File

@@ -6,16 +6,21 @@ import { setupServer } from 'msw/node';
import { useProviders, useCreateProvider, useUpdateProvider, useDeleteProvider } from '@/hooks/useProviders';
import type { Provider, CreateProviderInput, UpdateProviderInput } from '@/types';
// Mock antd message since it uses DOM APIs not available in jsdom
vi.mock('antd', () => ({
message: {
success: vi.fn(),
error: vi.fn(),
},
}));
const mockMessage = {
success: vi.fn(),
error: vi.fn(),
};
// Import the mocked message for assertions
import { message } from 'antd';
vi.mock('antd', async (importOriginal) => {
const original = await importOriginal<typeof import('antd')>();
return {
...original,
App: {
...original.App,
useApp: () => ({ message: mockMessage }),
},
};
});
// Test data
const mockProviders: Provider[] = [
@@ -149,7 +154,7 @@ describe('useCreateProvider', () => {
name: 'NewProvider',
});
expect(invalidateSpy).toHaveBeenCalledWith({ queryKey: ['providers'] });
expect(message.success).toHaveBeenCalledWith('供应商创建成功');
expect(mockMessage.success).toHaveBeenCalledWith('供应商创建成功');
});
it('calls message.error on failure', async () => {
@@ -174,7 +179,7 @@ describe('useCreateProvider', () => {
result.current.mutate(input);
await waitFor(() => expect(result.current.isError).toBe(true));
expect(message.error).toHaveBeenCalled();
expect(mockMessage.error).toHaveBeenCalled();
});
});
@@ -205,7 +210,7 @@ describe('useUpdateProvider', () => {
name: 'UpdatedProvider',
});
expect(invalidateSpy).toHaveBeenCalledWith({ queryKey: ['providers'] });
expect(message.success).toHaveBeenCalledWith('供应商更新成功');
expect(mockMessage.success).toHaveBeenCalledWith('供应商更新成功');
});
it('calls message.error on failure', async () => {
@@ -222,7 +227,7 @@ describe('useUpdateProvider', () => {
result.current.mutate({ id: 'provider-1', input: { name: 'Updated' } });
await waitFor(() => expect(result.current.isError).toBe(true));
expect(message.error).toHaveBeenCalled();
expect(mockMessage.error).toHaveBeenCalled();
});
});
@@ -248,7 +253,7 @@ describe('useDeleteProvider', () => {
await waitFor(() => expect(result.current.isSuccess).toBe(true));
expect(invalidateSpy).toHaveBeenCalledWith({ queryKey: ['providers'] });
expect(message.success).toHaveBeenCalledWith('供应商删除成功');
expect(mockMessage.success).toHaveBeenCalledWith('供应商删除成功');
});
it('calls message.error on failure', async () => {
@@ -265,6 +270,6 @@ describe('useDeleteProvider', () => {
result.current.mutate('provider-1');
await waitFor(() => expect(result.current.isError).toBe(true));
expect(message.error).toHaveBeenCalled();
expect(mockMessage.error).toHaveBeenCalled();
});
});