import { describe, it, expect, vi, beforeEach } from 'vitest';
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { UserManagement } from '../../src/components/UserManagement';
import { userClient } from '../../src/api/userClient';
// 完整的mock响应对象
const createMockResponse = (status: number, data?: any) => ({
status,
ok: status >= 200 && status < 300,
body: null,
bodyUsed: false,
statusText: status === 200 ? 'OK' : status === 201 ? 'Created' : status === 204 ? 'No Content' : 'Error',
headers: new Headers(),
url: '',
redirected: false,
type: 'basic' as ResponseType,
json: async () => data || {},
text: async () => '',
blob: async () => new Blob(),
arrayBuffer: async () => new ArrayBuffer(0),
formData: async () => new FormData(),
clone: function() { return this; }
});
// Mock API client
vi.mock('../../src/api/userClient', () => {
const mockUserClient = {
index: {
$get: vi.fn(() => Promise.resolve({ status: 200, body: null })),
$post: vi.fn(() => Promise.resolve({ status: 201, body: null })),
},
':id': {
$put: vi.fn(() => Promise.resolve({ status: 200, body: null })),
$delete: vi.fn(() => Promise.resolve({ status: 204, body: null })),
},
};
return {
userClient: mockUserClient,
};
});
// Mock toast
vi.mock('sonner', () => ({
toast: {
success: vi.fn(() => {}),
error: vi.fn(() => {}),
},
}));
const createTestQueryClient = () =>
new QueryClient({
defaultOptions: {
queries: {
retry: false,
},
},
});
const renderWithProviders = (component: React.ReactElement) => {
const queryClient = createTestQueryClient();
return render(
{component as any}
);
};
describe('用户管理集成测试', () => {
beforeEach(() => {
vi.clearAllMocks();
});
it('应该完成完整的用户CRUD流程', async () => {
const mockUsers = {
data: [
{
id: 1,
username: 'existinguser',
nickname: 'Existing User',
email: 'existing@example.com',
phone: '1234567890',
name: 'Existing Name',
isDisabled: 0,
createdAt: '2024-01-01T00:00:00Z',
roles: [{ id: 1, name: 'admin' }],
avatarFile: null,
},
],
pagination: {
total: 1,
page: 1,
pageSize: 10,
},
};
const { toast } = await import('sonner');
// Mock initial user list
(userClient.index.$get as any).mockResolvedValue(createMockResponse(200, mockUsers));
renderWithProviders();
// Wait for initial data to load
await waitFor(() => {
expect(screen.getByText('existinguser')).toBeInTheDocument();
});
// Test create user
const createButton = screen.getByTestId('create-user-button');
fireEvent.click(createButton);
// Fill create form
const usernameInput = screen.getByPlaceholderText('请输入用户名');
const passwordInput = screen.getByPlaceholderText('请输入密码');
const emailInput = screen.getByPlaceholderText('请输入邮箱');
fireEvent.change(usernameInput, { target: { value: 'newuser' } });
fireEvent.change(passwordInput, { target: { value: 'password123' } });
fireEvent.change(emailInput, { target: { value: 'new@example.com' } });
// Mock successful creation
(userClient.index.$post as any).mockResolvedValue(createMockResponse(201, { id: 2, username: 'newuser' }));
const submitButton = screen.getByTestId('create-user-submit-button');
fireEvent.click(submitButton);
await waitFor(() => {
expect(userClient.index.$post).toHaveBeenCalledWith({
json: {
username: 'newuser',
password: 'password123',
email: 'new@example.com',
nickname: null,
phone: null,
name: null,
isDisabled: 0,
},
});
expect(toast.success).toHaveBeenCalledWith('用户创建成功');
});
// Test edit user
const editButtons = screen.getAllByRole('button', { name: '编辑用户' });
fireEvent.click(editButtons[0]);
// Verify edit form is populated
await waitFor(() => {
expect(screen.getByDisplayValue('existinguser')).toBeInTheDocument();
});
// Update user
const updateUsernameInput = screen.getByDisplayValue('existinguser');
fireEvent.change(updateUsernameInput, { target: { value: 'updateduser' } });
// Mock successful update
(userClient[':id']['$put'] as any).mockResolvedValue(createMockResponse(200));
const updateButton = screen.getByText('更新用户');
fireEvent.click(updateButton);
await waitFor(() => {
expect(userClient[':id']['$put']).toHaveBeenCalledWith({
param: { id: 1 },
json: {
username: 'updateduser',
nickname: 'Existing User',
email: 'existing@example.com',
phone: '1234567890',
name: 'Existing Name',
password: undefined,
avatarFileId: undefined,
isDisabled: 0,
},
});
expect(toast.success).toHaveBeenCalledWith('用户更新成功');
});
// Test delete user
const deleteButtons = screen.getAllByRole('button', { name: '删除用户' });
fireEvent.click(deleteButtons[0]);
// Confirm deletion
expect(screen.getByText('确认删除')).toBeInTheDocument();
// Mock successful deletion
(userClient[':id']['$delete'] as any).mockResolvedValue({
status: 204,
});
const confirmDeleteButton = screen.getByText('删除');
fireEvent.click(confirmDeleteButton);
await waitFor(() => {
expect(userClient[':id']['$delete']).toHaveBeenCalledWith({
param: { id: 1 },
});
expect(toast.success).toHaveBeenCalledWith('用户删除成功');
});
});
it('应该优雅处理API错误', async () => {
const { userClient } = await import('../../src/api/userClient');
const { toast } = await import('sonner');
// Mock API error
(userClient.index.$get as any).mockRejectedValue(new Error('API Error'));
renderWithProviders();
// Should handle error without crashing
await waitFor(() => {
expect(screen.getByText('用户管理')).toBeInTheDocument();
});
// Test create user error
const createButton = screen.getByText('创建用户');
fireEvent.click(createButton);
const usernameInput = screen.getByPlaceholderText('请输入用户名');
const passwordInput = screen.getByPlaceholderText('请输入密码');
fireEvent.change(usernameInput, { target: { value: 'testuser' } });
fireEvent.change(passwordInput, { target: { value: 'password' } });
// Mock creation error
(userClient.index.$post as any).mockRejectedValue(new Error('Creation failed'));
const submitButton = screen.getByTestId('create-user-submit-button');
fireEvent.click(submitButton);
await waitFor(() => {
expect(toast.error).toHaveBeenCalledWith('创建失败,请重试');
});
});
it('应该处理搜索和过滤器集成', async () => {
const { userClient } = await import('../../src/api/userClient');
const mockUsers = {
data: [],
pagination: { total: 0, page: 1, pageSize: 10 },
};
(userClient.index.$get as any).mockResolvedValue(createMockResponse(200, mockUsers));
renderWithProviders();
// Test search
const searchInput = screen.getByPlaceholderText('搜索用户名、昵称或邮箱...');
fireEvent.change(searchInput, { target: { value: 'searchterm' } });
await waitFor(() => {
expect(userClient.index.$get).toHaveBeenCalledWith({
query: {
page: 1,
pageSize: 10,
keyword: 'searchterm',
filters: undefined,
},
});
});
// Test filter
const filterButton = screen.getByTestId('advanced-filter-button');
fireEvent.click(filterButton);
// Wait for filter panel to appear
await waitFor(() => {
expect(screen.getByTestId('status-filter-trigger')).toBeInTheDocument();
}, { timeout: 2000 });
// Apply status filter
const statusSelect = screen.getByTestId('status-filter-trigger');
fireEvent.click(statusSelect);
const enabledOption = screen.getByTestId('status-enabled-option');
fireEvent.click(enabledOption);
await waitFor(() => {
expect(userClient.index.$get).toHaveBeenCalledWith({
query: {
page: 1,
pageSize: 10,
keyword: 'searchterm',
filters: expect.stringContaining('isDisabled'),
},
});
});
});
});