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 { MerchantSelector } from '../../src/components/MerchantSelector';
import { merchantClient } from '../../src/api/merchantClient';
// 完整的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/merchantClient', () => {
const mockMerchantClient = {
$get: vi.fn(() => Promise.resolve({
status: 200,
body: null,
json: async () => ({ data: [], pagination: { total: 0, page: 1, pageSize: 100 } })
})),
};
const mockMerchantClientManager = {
get: vi.fn(() => mockMerchantClient),
};
return {
merchantClientManager: mockMerchantClientManager,
merchantClient: mockMerchantClient,
};
});
const createTestQueryClient = () =>
new QueryClient({
defaultOptions: {
queries: {
retry: false,
},
},
});
const renderWithProviders = (component: React.ReactElement) => {
const queryClient = createTestQueryClient();
return render(
{component}
);
};
describe('MerchantSelector', () => {
beforeEach(() => {
vi.clearAllMocks();
});
it('应该渲染商户选择器', () => {
renderWithProviders();
const selectTrigger = screen.getByTestId('merchant-selector');
expect(selectTrigger).toBeInTheDocument();
});
it('应该显示自定义占位符', () => {
renderWithProviders(
);
const selectTrigger = screen.getByTestId('merchant-selector');
expect(selectTrigger).toHaveTextContent('请选择商户');
});
it('应该调用API获取商户列表', async () => {
const mockMerchants = [
{ id: 1, name: '商户A' },
{ id: 2, name: '商户B' }
];
(merchantClient.$get as any).mockResolvedValueOnce(
createMockResponse(200, {
data: mockMerchants,
pagination: { total: 2, page: 1, pageSize: 100 }
})
);
renderWithProviders();
await waitFor(() => {
expect(merchantClient.$get).toHaveBeenCalledWith({
query: { page: 1, pageSize: 100 }
});
});
});
it('应该显示商户列表', async () => {
const mockMerchants = [
{ id: 1, name: '商户A' },
{ id: 2, name: '商户B' }
];
(merchantClient.$get as any).mockResolvedValueOnce(
createMockResponse(200, {
data: mockMerchants,
pagination: { total: 2, page: 1, pageSize: 100 }
})
);
renderWithProviders();
await waitFor(() => {
expect(merchantClient.$get).toHaveBeenCalledWith({
query: { page: 1, pageSize: 100 }
});
});
// 验证API调用,不测试下拉菜单的UI交互
expect(merchantClient.$get).toHaveBeenCalledTimes(1);
});
it('应该处理API错误', async () => {
(merchantClient.$get as any).mockRejectedValueOnce(new Error('API错误'));
renderWithProviders();
// 验证组件渲染
const selectTrigger = screen.getByTestId('merchant-selector');
expect(selectTrigger).toBeInTheDocument();
// 等待API调用完成
await waitFor(() => {
expect(merchantClient.$get).toHaveBeenCalled();
});
});
it('应该支持禁用状态', () => {
renderWithProviders(
);
const selectTrigger = screen.getByTestId('merchant-selector');
expect(selectTrigger).toBeDisabled();
});
it('应该支持值选择', async () => {
const mockMerchants = [
{ id: 1, name: '商户A' },
{ id: 2, name: '商户B' }
];
(merchantClient.$get as any).mockResolvedValueOnce(
createMockResponse(200, {
data: mockMerchants,
pagination: { total: 2, page: 1, pageSize: 100 }
})
);
const mockOnChange = vi.fn();
renderWithProviders(
);
await waitFor(() => {
expect(merchantClient.$get).toHaveBeenCalledWith({
query: { page: 1, pageSize: 100 }
});
});
// 验证API调用,不测试下拉菜单的UI交互
expect(merchantClient.$get).toHaveBeenCalledTimes(1);
});
});