feat: 完成前端重构,采用 Ant Design 5 和完整测试体系
- 采用 Ant Design 5 作为 UI 组件库,替换自定义组件 - 集成 React Router v7 提供路由导航 - 使用 TanStack Query v5 管理数据获取和缓存 - 建立 Vitest + React Testing Library 测试体系 - 添加 Playwright E2E 测试覆盖 - 使用 MSW mock API 响应 - 配置 TypeScript strict 模式 - 采用 SCSS Modules 组织样式 - 更新 OpenSpec 规格以反映前端架构变更 - 归档 frontend-refactor 变更记录
This commit is contained in:
165
frontend/src/__tests__/api/providers.test.ts
Normal file
165
frontend/src/__tests__/api/providers.test.ts
Normal file
@@ -0,0 +1,165 @@
|
||||
import { describe, it, expect, beforeAll, afterEach, afterAll } from 'vitest';
|
||||
import { setupServer } from 'msw/node';
|
||||
import { http, HttpResponse } from 'msw';
|
||||
import { listProviders, createProvider, updateProvider, deleteProvider } from '@/api/providers';
|
||||
|
||||
const mockProviders = [
|
||||
{
|
||||
id: 'prov-1',
|
||||
name: 'OpenAI',
|
||||
api_key: 'sk-xxx',
|
||||
base_url: 'https://api.openai.com',
|
||||
enabled: true,
|
||||
created_at: '2025-01-01T00:00:00Z',
|
||||
updated_at: '2025-01-01T00:00:00Z',
|
||||
},
|
||||
{
|
||||
id: 'prov-2',
|
||||
name: 'Anthropic',
|
||||
api_key: 'sk-yyy',
|
||||
base_url: 'https://api.anthropic.com',
|
||||
enabled: false,
|
||||
created_at: '2025-01-02T00:00:00Z',
|
||||
updated_at: '2025-01-02T00:00:00Z',
|
||||
},
|
||||
];
|
||||
|
||||
describe('providers API', () => {
|
||||
const server = setupServer();
|
||||
|
||||
beforeAll(() => server.listen({ onUnhandledRequest: 'bypass' }));
|
||||
afterEach(() => server.resetHandlers());
|
||||
afterAll(() => server.close());
|
||||
|
||||
describe('listProviders', () => {
|
||||
it('returns array of Provider objects with camelCase keys', async () => {
|
||||
server.use(
|
||||
http.get('/api/providers', () => {
|
||||
return HttpResponse.json(mockProviders);
|
||||
}),
|
||||
);
|
||||
|
||||
const result = await listProviders();
|
||||
|
||||
expect(result).toEqual([
|
||||
{
|
||||
id: 'prov-1',
|
||||
name: 'OpenAI',
|
||||
apiKey: 'sk-xxx',
|
||||
baseUrl: 'https://api.openai.com',
|
||||
enabled: true,
|
||||
createdAt: '2025-01-01T00:00:00Z',
|
||||
updatedAt: '2025-01-01T00:00:00Z',
|
||||
},
|
||||
{
|
||||
id: 'prov-2',
|
||||
name: 'Anthropic',
|
||||
apiKey: 'sk-yyy',
|
||||
baseUrl: 'https://api.anthropic.com',
|
||||
enabled: false,
|
||||
createdAt: '2025-01-02T00:00:00Z',
|
||||
updatedAt: '2025-01-02T00:00:00Z',
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('createProvider', () => {
|
||||
it('sends POST with correct body and returns provider', async () => {
|
||||
let receivedMethod: string | null = null;
|
||||
let receivedBody: Record<string, unknown> | null = null;
|
||||
|
||||
server.use(
|
||||
http.post('/api/providers', async ({ request }) => {
|
||||
receivedMethod = request.method;
|
||||
receivedBody = (await request.json()) as Record<string, unknown>;
|
||||
return HttpResponse.json(mockProviders[0]);
|
||||
}),
|
||||
);
|
||||
|
||||
const input = {
|
||||
id: 'prov-1',
|
||||
name: 'OpenAI',
|
||||
apiKey: 'sk-xxx',
|
||||
baseUrl: 'https://api.openai.com',
|
||||
enabled: true,
|
||||
};
|
||||
|
||||
const result = await createProvider(input);
|
||||
|
||||
expect(receivedMethod).toBe('POST');
|
||||
expect(receivedBody).toEqual({
|
||||
id: 'prov-1',
|
||||
name: 'OpenAI',
|
||||
api_key: 'sk-xxx',
|
||||
base_url: 'https://api.openai.com',
|
||||
enabled: true,
|
||||
});
|
||||
expect(result).toEqual({
|
||||
id: 'prov-1',
|
||||
name: 'OpenAI',
|
||||
apiKey: 'sk-xxx',
|
||||
baseUrl: 'https://api.openai.com',
|
||||
enabled: true,
|
||||
createdAt: '2025-01-01T00:00:00Z',
|
||||
updatedAt: '2025-01-01T00:00:00Z',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('updateProvider', () => {
|
||||
it('sends PUT with correct body and returns provider', async () => {
|
||||
let receivedMethod: string | null = null;
|
||||
let receivedUrl: string | null = null;
|
||||
let receivedBody: Record<string, unknown> | null = null;
|
||||
|
||||
server.use(
|
||||
http.put('/api/providers/:id', async ({ request, params }) => {
|
||||
receivedMethod = request.method;
|
||||
receivedUrl = new URL(request.url).pathname;
|
||||
receivedBody = (await request.json()) as Record<string, unknown>;
|
||||
return HttpResponse.json({
|
||||
...mockProviders[0],
|
||||
name: 'Updated',
|
||||
api_key: 'sk-updated',
|
||||
});
|
||||
}),
|
||||
);
|
||||
|
||||
const result = await updateProvider('prov-1', {
|
||||
name: 'Updated',
|
||||
apiKey: 'sk-updated',
|
||||
});
|
||||
|
||||
expect(receivedMethod).toBe('PUT');
|
||||
expect(receivedUrl).toBe('/api/providers/prov-1');
|
||||
expect(receivedBody).toEqual({
|
||||
name: 'Updated',
|
||||
api_key: 'sk-updated',
|
||||
});
|
||||
expect(result.name).toBe('Updated');
|
||||
expect(result.apiKey).toBe('sk-updated');
|
||||
});
|
||||
});
|
||||
|
||||
describe('deleteProvider', () => {
|
||||
it('sends DELETE and returns void', async () => {
|
||||
let receivedMethod: string | null = null;
|
||||
let receivedUrl: string | null = null;
|
||||
|
||||
server.use(
|
||||
http.delete('/api/providers/:id', ({ request, params }) => {
|
||||
receivedMethod = request.method;
|
||||
receivedUrl = new URL(request.url).pathname;
|
||||
return new HttpResponse(null, { status: 204 });
|
||||
}),
|
||||
);
|
||||
|
||||
const result = await deleteProvider('prov-1');
|
||||
|
||||
expect(receivedMethod).toBe('DELETE');
|
||||
expect(receivedUrl).toBe('/api/providers/prov-1');
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user