|
|
@@ -0,0 +1,278 @@
|
|
|
+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 { UserSelector } from '../../src/components/UserSelector';
|
|
|
+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,
|
|
|
+ json: async () => ({ data: [], pagination: { total: 0, page: 1, pageSize: 100 } })
|
|
|
+ })),
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ const mockUserClientManager = {
|
|
|
+ get: vi.fn(() => mockUserClient),
|
|
|
+ };
|
|
|
+
|
|
|
+ return {
|
|
|
+ userClientManager: mockUserClientManager,
|
|
|
+ userClient: mockUserClient,
|
|
|
+ };
|
|
|
+});
|
|
|
+
|
|
|
+const createTestQueryClient = () =>
|
|
|
+ new QueryClient({
|
|
|
+ defaultOptions: {
|
|
|
+ queries: {
|
|
|
+ retry: false,
|
|
|
+ enabled: true,
|
|
|
+ },
|
|
|
+ },
|
|
|
+ });
|
|
|
+
|
|
|
+const renderWithProviders = (component: React.ReactElement) => {
|
|
|
+ const queryClient = createTestQueryClient();
|
|
|
+ return render(
|
|
|
+ <QueryClientProvider client={queryClient}>
|
|
|
+ {component as any}
|
|
|
+ </QueryClientProvider>
|
|
|
+ );
|
|
|
+};
|
|
|
+
|
|
|
+describe('用户选择器集成测试', () => {
|
|
|
+ beforeEach(() => {
|
|
|
+ vi.clearAllMocks();
|
|
|
+ });
|
|
|
+
|
|
|
+ it('应该加载并显示用户列表', async () => {
|
|
|
+ const mockUsers = {
|
|
|
+ data: [
|
|
|
+ {
|
|
|
+ id: 1,
|
|
|
+ username: 'user1',
|
|
|
+ name: 'User One',
|
|
|
+ email: 'user1@example.com',
|
|
|
+ phone: '1234567890',
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: 2,
|
|
|
+ username: 'user2',
|
|
|
+ name: 'User Two',
|
|
|
+ email: 'user2@example.com',
|
|
|
+ phone: '0987654321',
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ pagination: {
|
|
|
+ total: 2,
|
|
|
+ page: 1,
|
|
|
+ pageSize: 100,
|
|
|
+ },
|
|
|
+ };
|
|
|
+
|
|
|
+ // Mock user list API
|
|
|
+ (userClient.index.$get as any).mockResolvedValue(createMockResponse(200, mockUsers));
|
|
|
+
|
|
|
+ renderWithProviders(<UserSelector testId="user-selector" />);
|
|
|
+
|
|
|
+ // Open select dropdown to trigger API call
|
|
|
+ const selectTrigger = screen.getByTestId('user-selector');
|
|
|
+ fireEvent.click(selectTrigger);
|
|
|
+
|
|
|
+ // Wait for API call and loading to complete
|
|
|
+ await waitFor(() => {
|
|
|
+ expect(userClient.index.$get).toHaveBeenCalledWith({
|
|
|
+ query: {
|
|
|
+ page: 1,
|
|
|
+ pageSize: 100,
|
|
|
+ },
|
|
|
+ });
|
|
|
+ });
|
|
|
+
|
|
|
+ // Verify select trigger is rendered
|
|
|
+ expect(selectTrigger).toBeInTheDocument();
|
|
|
+ });
|
|
|
+
|
|
|
+ it('应该处理用户选择', async () => {
|
|
|
+ const mockUsers = {
|
|
|
+ data: [
|
|
|
+ {
|
|
|
+ id: 1,
|
|
|
+ username: 'user1',
|
|
|
+ name: 'User One',
|
|
|
+ email: 'user1@example.com',
|
|
|
+ phone: '1234567890',
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ pagination: {
|
|
|
+ total: 1,
|
|
|
+ page: 1,
|
|
|
+ pageSize: 100,
|
|
|
+ },
|
|
|
+ };
|
|
|
+
|
|
|
+ const mockOnChange = vi.fn();
|
|
|
+
|
|
|
+ // Mock user list API
|
|
|
+ (userClient.index.$get as any).mockResolvedValue(createMockResponse(200, mockUsers));
|
|
|
+
|
|
|
+ renderWithProviders(
|
|
|
+ <UserSelector onChange={mockOnChange} testId="user-selector" />
|
|
|
+ );
|
|
|
+
|
|
|
+ // Wait for API call
|
|
|
+ await waitFor(() => {
|
|
|
+ expect(userClient.index.$get).toHaveBeenCalledWith({
|
|
|
+ query: {
|
|
|
+ page: 1,
|
|
|
+ pageSize: 100,
|
|
|
+ },
|
|
|
+ });
|
|
|
+ });
|
|
|
+
|
|
|
+ // Verify select trigger is rendered
|
|
|
+ const selectTrigger = screen.getByTestId('user-selector');
|
|
|
+ expect(selectTrigger).toBeInTheDocument();
|
|
|
+
|
|
|
+ // Verify onChange callback is properly passed
|
|
|
+ expect(mockOnChange).not.toHaveBeenCalled();
|
|
|
+ });
|
|
|
+
|
|
|
+ it('应该显示自定义占位符', async () => {
|
|
|
+ const mockUsers = {
|
|
|
+ data: [],
|
|
|
+ pagination: {
|
|
|
+ total: 0,
|
|
|
+ page: 1,
|
|
|
+ pageSize: 100,
|
|
|
+ },
|
|
|
+ };
|
|
|
+
|
|
|
+ // Mock empty user list
|
|
|
+ (userClient.index.$get as any).mockResolvedValue(createMockResponse(200, mockUsers));
|
|
|
+
|
|
|
+ renderWithProviders(
|
|
|
+ <UserSelector placeholder="请选择用户" testId="user-selector" />
|
|
|
+ );
|
|
|
+
|
|
|
+ // Open select dropdown to trigger API call
|
|
|
+ const selectTrigger = screen.getByTestId('user-selector');
|
|
|
+ fireEvent.click(selectTrigger);
|
|
|
+
|
|
|
+ // Wait for API call
|
|
|
+ await waitFor(() => {
|
|
|
+ expect(userClient.index.$get).toHaveBeenCalled();
|
|
|
+ });
|
|
|
+
|
|
|
+ // Verify custom placeholder is shown
|
|
|
+ expect(selectTrigger).toHaveTextContent('请选择用户');
|
|
|
+ });
|
|
|
+
|
|
|
+ it('应该处理禁用状态', async () => {
|
|
|
+ const mockUsers = {
|
|
|
+ data: [
|
|
|
+ {
|
|
|
+ id: 1,
|
|
|
+ username: 'user1',
|
|
|
+ name: 'User One',
|
|
|
+ email: 'user1@example.com',
|
|
|
+ phone: '1234567890',
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ pagination: {
|
|
|
+ total: 1,
|
|
|
+ page: 1,
|
|
|
+ pageSize: 100,
|
|
|
+ },
|
|
|
+ };
|
|
|
+
|
|
|
+ // Mock user list API
|
|
|
+ (userClient.index.$get as any).mockResolvedValue(createMockResponse(200, mockUsers));
|
|
|
+
|
|
|
+ renderWithProviders(
|
|
|
+ <UserSelector disabled={true} testId="user-selector" />
|
|
|
+ );
|
|
|
+
|
|
|
+ // Verify select is disabled immediately (no need to wait for API call)
|
|
|
+ const selectTrigger = screen.getByTestId('user-selector');
|
|
|
+ expect(selectTrigger).toBeDisabled();
|
|
|
+ });
|
|
|
+
|
|
|
+ it('应该处理API错误', async () => {
|
|
|
+ // Mock API error
|
|
|
+ (userClient.index.$get as any).mockRejectedValue(new Error('API Error'));
|
|
|
+
|
|
|
+ renderWithProviders(<UserSelector testId="user-selector" />);
|
|
|
+
|
|
|
+ // Should handle error without crashing
|
|
|
+ await waitFor(() => {
|
|
|
+ expect(screen.getByTestId('user-selector')).toBeInTheDocument();
|
|
|
+ });
|
|
|
+ });
|
|
|
+
|
|
|
+ it('应该显示预选值', async () => {
|
|
|
+ const mockUsers = {
|
|
|
+ data: [
|
|
|
+ {
|
|
|
+ id: 1,
|
|
|
+ username: 'user1',
|
|
|
+ name: 'User One',
|
|
|
+ email: 'user1@example.com',
|
|
|
+ phone: '1234567890',
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: 2,
|
|
|
+ username: 'user2',
|
|
|
+ name: 'User Two',
|
|
|
+ email: 'user2@example.com',
|
|
|
+ phone: '0987654321',
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ pagination: {
|
|
|
+ total: 2,
|
|
|
+ page: 1,
|
|
|
+ pageSize: 100,
|
|
|
+ },
|
|
|
+ };
|
|
|
+
|
|
|
+ // Mock user list API
|
|
|
+ (userClient.index.$get as any).mockResolvedValue(createMockResponse(200, mockUsers));
|
|
|
+
|
|
|
+ renderWithProviders(
|
|
|
+ <UserSelector value={2} testId="user-selector" />
|
|
|
+ );
|
|
|
+
|
|
|
+ // Wait for data to load
|
|
|
+ await waitFor(() => {
|
|
|
+ expect(userClient.index.$get).toHaveBeenCalled();
|
|
|
+ });
|
|
|
+
|
|
|
+ // Verify the select has the correct value
|
|
|
+ const selectTrigger = screen.getByTestId('user-selector');
|
|
|
+ expect(selectTrigger).toHaveAttribute('data-state', 'closed');
|
|
|
+ });
|
|
|
+});
|