| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403 |
- 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 { MerchantManagement } from '../../src/components/MerchantManagement';
- import { merchantClientManager } 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 = {
- 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 })),
- },
- };
- const mockMerchantClientManager = {
- get: vi.fn(() => mockMerchantClient),
- };
- return {
- merchantClientManager: mockMerchantClientManager,
- merchantClient: mockMerchantClient,
- };
- });
- // 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(
- <QueryClientProvider client={queryClient}>
- {component as any}
- </QueryClientProvider>
- );
- };
- describe('商户管理集成测试', () => {
- beforeEach(() => {
- vi.clearAllMocks();
- });
- it('应该完成完整的商户CRUD流程', async () => {
- const mockMerchants = {
- data: [
- {
- id: 1,
- name: '测试商户',
- username: 'testmerchant',
- realname: '张三',
- phone: '13800138000',
- state: 1,
- loginNum: 5,
- createdAt: '2024-01-01T00:00:00Z',
- updatedAt: '2024-01-01T00:00:00Z',
- lastLoginTime: 1704067200,
- lastLoginIp: '192.168.1.1',
- rsaPublicKey: '',
- aesKey: '',
- },
- ],
- pagination: {
- total: 1,
- page: 1,
- pageSize: 10,
- },
- };
- const { toast } = await import('sonner');
- // Mock initial merchant list
- (merchantClientManager.get().index.$get as any).mockResolvedValue(createMockResponse(200, mockMerchants));
- renderWithProviders(<MerchantManagement />);
- // Wait for initial data to load
- await waitFor(() => {
- expect(screen.getByText('testmerchant')).toBeInTheDocument();
- });
- // Test create merchant
- const createButton = screen.getByTestId('create-merchant-button');
- fireEvent.click(createButton);
- // Fill create form
- const nameInput = screen.getByPlaceholderText('请输入商户名称');
- const usernameInput = screen.getByPlaceholderText('请输入用户名');
- const passwordInput = screen.getByPlaceholderText('请输入密码');
- const phoneInput = screen.getByPlaceholderText('请输入手机号');
- fireEvent.change(nameInput, { target: { value: '新商户' } });
- fireEvent.change(usernameInput, { target: { value: 'newmerchant' } });
- fireEvent.change(passwordInput, { target: { value: 'password123' } });
- fireEvent.change(phoneInput, { target: { value: '13800138000' } });
- // Mock successful creation
- (merchantClientManager.get().index.$post as any).mockResolvedValue(createMockResponse(201, { id: 2, username: 'newmerchant' }));
- const submitButton = screen.getByTestId('create-submit-button');
- fireEvent.click(submitButton);
- await waitFor(() => {
- expect(merchantClientManager.get().index.$post).toHaveBeenCalledWith({
- json: {
- name: '新商户',
- username: 'newmerchant',
- password: 'password123',
- phone: '13800138000',
- realname: '',
- state: 2,
- rsaPublicKey: '',
- aesKey: '',
- },
- });
- expect(toast.success).toHaveBeenCalledWith('商户创建成功');
- });
- // Test edit merchant
- const editButtons = screen.getAllByTestId('edit-button-1');
- fireEvent.click(editButtons[0]);
- // Verify edit form is populated
- await waitFor(() => {
- expect(screen.getByDisplayValue('测试商户')).toBeInTheDocument();
- });
- // Update merchant
- const updateNameInput = screen.getByDisplayValue('测试商户');
- fireEvent.change(updateNameInput, { target: { value: '更新商户' } });
- // Mock successful update
- (merchantClientManager.get()[':id']['$put'] as any).mockResolvedValue(createMockResponse(200));
- const updateButton = screen.getByTestId('update-submit-button');
- fireEvent.click(updateButton);
- await waitFor(() => {
- expect(merchantClientManager.get()[':id']['$put']).toHaveBeenCalledWith({
- param: { id: 1 },
- json: {
- name: '更新商户',
- username: 'testmerchant',
- phone: '13800138000',
- realname: '张三',
- password: undefined,
- state: 1,
- rsaPublicKey: '',
- aesKey: '',
- },
- });
- expect(toast.success).toHaveBeenCalledWith('商户更新成功');
- });
- // Test delete merchant
- const deleteButtons = screen.getAllByTestId('delete-button-1');
- fireEvent.click(deleteButtons[0]);
- // Confirm deletion
- expect(screen.getByText('确认删除')).toBeInTheDocument();
- // Mock successful deletion
- (merchantClientManager.get()[':id']['$delete'] as any).mockResolvedValue({
- status: 204,
- });
- const confirmDeleteButton = screen.getByText('删除');
- fireEvent.click(confirmDeleteButton);
- await waitFor(() => {
- expect(merchantClientManager.get()[':id']['$delete']).toHaveBeenCalledWith({
- param: { id: 1 },
- });
- expect(toast.success).toHaveBeenCalledWith('商户删除成功');
- });
- });
- it('应该优雅处理API错误', async () => {
- const { toast } = await import('sonner');
- // Mock API error
- (merchantClientManager.get().index.$get as any).mockRejectedValue(new Error('API Error'));
- renderWithProviders(<MerchantManagement />);
- // Should handle error without crashing
- await waitFor(() => {
- expect(screen.getByText('商户管理')).toBeInTheDocument();
- });
- // Test create merchant error
- const createButton = screen.getByTestId('create-merchant-button');
- fireEvent.click(createButton);
- // 等待对话框打开
- await waitFor(() => {
- expect(screen.getByRole('dialog')).toBeInTheDocument();
- });
- // 填写必填字段
- const nameInput = screen.getByPlaceholderText('请输入商户名称');
- const usernameInput = screen.getByPlaceholderText('请输入用户名');
- const passwordInput = screen.getByPlaceholderText('请输入密码');
- const phoneInput = screen.getByPlaceholderText('请输入手机号');
- fireEvent.change(nameInput, { target: { value: '测试商户' } });
- fireEvent.change(usernameInput, { target: { value: 'testmerchant' } });
- fireEvent.change(passwordInput, { target: { value: 'password123' } });
- fireEvent.change(phoneInput, { target: { value: '13800138000' } });
- // Mock creation error
- (merchantClientManager.get().index.$post as any).mockRejectedValue(new Error('创建商户失败'));
- const submitButton = screen.getByTestId('create-submit-button');
- fireEvent.click(submitButton);
- await waitFor(() => {
- expect(toast.error).toHaveBeenCalledWith('创建商户失败');
- });
- });
- it('应该处理搜索功能', async () => {
- const mockMerchants = {
- data: [],
- pagination: { total: 0, page: 1, pageSize: 10 },
- };
- (merchantClientManager.get().index.$get as any).mockResolvedValue(createMockResponse(200, mockMerchants));
- renderWithProviders(<MerchantManagement />);
- // 等待数据加载完成
- await waitFor(() => {
- expect(screen.getByText('商户列表')).toBeInTheDocument();
- });
- // Test search
- const searchInput = screen.getByPlaceholderText('搜索商户名称、用户名、手机号...');
- fireEvent.change(searchInput, { target: { value: '搜索关键词' } });
- const searchButton = screen.getByTestId('search-button');
- fireEvent.click(searchButton);
- await waitFor(() => {
- expect(merchantClientManager.get().index.$get).toHaveBeenCalledWith({
- query: {
- page: 1,
- pageSize: 10,
- keyword: '搜索关键词',
- },
- });
- });
- });
- it('应该显示商户详情', async () => {
- const mockMerchants = {
- data: [
- {
- id: 1,
- name: '测试商户',
- username: 'testmerchant',
- realname: '张三',
- phone: '13800138000',
- state: 1,
- loginNum: 5,
- createdAt: '2024-01-01T00:00:00Z',
- updatedAt: '2024-01-01T00:00:00Z',
- lastLoginTime: 1704067200,
- lastLoginIp: '192.168.1.1',
- rsaPublicKey: '',
- aesKey: '',
- },
- ],
- pagination: {
- total: 1,
- page: 1,
- pageSize: 10,
- },
- };
- (merchantClientManager.get().index.$get as any).mockResolvedValue(createMockResponse(200, mockMerchants));
- renderWithProviders(<MerchantManagement />);
- // Wait for data to load
- await waitFor(() => {
- expect(screen.getByText('testmerchant')).toBeInTheDocument();
- });
- // Test view details
- const viewButtons = screen.getAllByTestId('view-detail-button-1');
- fireEvent.click(viewButtons[0]);
- // Verify detail dialog appears
- await waitFor(() => {
- expect(screen.getByText('商户详情')).toBeInTheDocument();
- });
- // 验证详情内容 - 只验证对话框标题
- expect(screen.getByText('商户详情')).toBeInTheDocument();
- });
- it('应该正确处理分页', async () => {
- const mockMerchantsPage1 = {
- data: [
- { id: '1', name: '商户1', username: 'merchant1', phone: '13800000001', status: 'active', createdAt: '2024-01-01T00:00:00Z' },
- { id: '2', name: '商户2', username: 'merchant2', phone: '13800000002', status: 'active', createdAt: '2024-01-01T00:00:00Z' }
- ],
- pagination: { total: 25, page: 1, pageSize: 10 },
- };
- const mockMerchantsPage2 = {
- data: [
- { id: '3', name: '商户3', username: 'merchant3', phone: '13800000003', status: 'active', createdAt: '2024-01-01T00:00:00Z' },
- { id: '4', name: '商户4', username: 'merchant4', phone: '13800000004', status: 'active', createdAt: '2024-01-01T00:00:00Z' }
- ],
- pagination: { total: 25, page: 2, pageSize: 10 },
- };
- // 设置 mock 响应 - 先只用一个响应
- (merchantClientManager.get().index.$get as any)
- .mockResolvedValue(createMockResponse(200, mockMerchantsPage1));
- renderWithProviders(<MerchantManagement />);
- // Wait for data to load
- await waitFor(() => {
- expect(screen.getByText('商户列表')).toBeInTheDocument();
- });
- // Debug: Check if merchants are displayed
- const merchantNames = screen.queryAllByText(/商户[0-9]/);
- console.log('Merchant names found:', merchantNames.length);
- console.log('Merchant names:', merchantNames.map(el => el.textContent));
- // Debug: Check what pagination elements are rendered
- const allLinks = screen.getAllByRole('link');
- console.log('All links:', allLinks.map(link => ({
- text: link.textContent,
- ariaLabel: link.getAttribute('aria-label'),
- ariaDisabled: link.getAttribute('aria-disabled')
- })));
- // Debug: Check if pagination info is displayed
- const paginationInfo = screen.queryByText(/共.*条记录/);
- console.log('Pagination info:', paginationInfo?.textContent);
- // Test pagination - click next page
- const nextPageButton = screen.getByLabelText(/next/i);
- fireEvent.click(nextPageButton);
- // 等待分页请求完成 - 增加等待时间
- await waitFor(() => {
- expect(merchantClientManager.get().index.$get).toHaveBeenCalledTimes(2);
- }, { timeout: 5000 });
- // 验证第二次调用的参数
- const calls = (merchantClientManager.get().index.$get as any).mock.calls;
- expect(calls[1][0]).toEqual({
- query: {
- page: 2,
- pageSize: 10,
- keyword: '',
- },
- });
- });
- });
|