FileSelector.test.tsx 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. import { describe, it, expect, vi, beforeEach } from 'vitest';
  2. import { render, screen, fireEvent, waitFor } from '@testing-library/react';
  3. import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
  4. import FileSelector from '../../src/components/FileSelector';
  5. // 完整的mock响应对象
  6. const createMockResponse = (status: number, data?: any) => ({
  7. status,
  8. ok: status >= 200 && status < 300,
  9. body: null,
  10. bodyUsed: false,
  11. statusText: status === 200 ? 'OK' : status === 201 ? 'Created' : status === 204 ? 'No Content' : 'Error',
  12. headers: new Headers(),
  13. url: '',
  14. redirected: false,
  15. type: 'basic' as ResponseType,
  16. json: async () => data || {},
  17. text: async () => '',
  18. blob: async () => new Blob(),
  19. arrayBuffer: async () => new ArrayBuffer(0),
  20. formData: async () => new FormData(),
  21. clone: function() { return this; }
  22. });
  23. // Mock API客户端
  24. vi.mock('../../src/api/fileClient', () => {
  25. const mockFileClient = {
  26. index: {
  27. $get: vi.fn(() => Promise.resolve(createMockResponse(200, {
  28. data: [],
  29. pagination: { current: 1, pageSize: 50, total: 0 }
  30. }))),
  31. },
  32. ':id': {
  33. $get: vi.fn(() => Promise.resolve(createMockResponse(200, {}))),
  34. },
  35. };
  36. const mockFileClientManager = {
  37. get: vi.fn(() => mockFileClient),
  38. };
  39. return {
  40. fileClientManager: mockFileClientManager,
  41. fileClient: mockFileClient,
  42. };
  43. });
  44. // Mock 文件上传组件
  45. vi.mock('../../src/components/MinioUploader', () => ({
  46. default: () => <div data-testid="minio-uploader">MinioUploader</div>,
  47. }));
  48. describe('FileSelector', () => {
  49. let queryClient: QueryClient;
  50. beforeEach(() => {
  51. queryClient = new QueryClient({
  52. defaultOptions: {
  53. queries: { retry: false },
  54. mutations: { retry: false },
  55. },
  56. });
  57. vi.clearAllMocks();
  58. });
  59. const renderWithQueryClient = (component: React.ReactElement) => {
  60. return render(
  61. <QueryClientProvider client={queryClient}>
  62. {component}
  63. </QueryClientProvider>
  64. );
  65. };
  66. const mockFiles = [
  67. {
  68. id: 1,
  69. name: 'test-image.jpg',
  70. type: 'image/jpeg',
  71. size: 1024,
  72. fullUrl: 'http://example.com/test-image.jpg',
  73. uploadTime: '2024-01-01T00:00:00Z',
  74. },
  75. {
  76. id: 2,
  77. name: 'test-document.pdf',
  78. type: 'application/pdf',
  79. size: 2048,
  80. fullUrl: 'http://example.com/test-document.pdf',
  81. uploadTime: '2024-01-01T00:00:00Z',
  82. },
  83. ];
  84. it('应该渲染文件选择器', () => {
  85. renderWithQueryClient(
  86. <FileSelector value={null} onChange={() => {}} />
  87. );
  88. expect(screen.getByTestId('file-selector-button')).toBeInTheDocument();
  89. });
  90. it('应该打开选择对话框', async () => {
  91. const { fileClient } = await import('../../src/api/fileClient');
  92. (fileClient.index.$get as any).mockResolvedValue(createMockResponse(200, {
  93. data: mockFiles,
  94. pagination: { current: 1, pageSize: 50, total: 2 }
  95. }));
  96. renderWithQueryClient(
  97. <FileSelector value={null} onChange={() => {}} />
  98. );
  99. const selectButton = screen.getByTestId('file-selector-button');
  100. fireEvent.click(selectButton);
  101. await waitFor(() => {
  102. expect(screen.getByTestId('file-selector-dialog')).toBeInTheDocument();
  103. expect(screen.getByRole('heading', { name: '选择文件' })).toBeInTheDocument();
  104. expect(screen.getByText('上传新文件或从已有文件中选择')).toBeInTheDocument();
  105. });
  106. });
  107. it('应该显示已选文件预览', async () => {
  108. const { fileClient } = await import('../../src/api/fileClient');
  109. (fileClient[':id'].$get as any).mockResolvedValue(createMockResponse(200, mockFiles[0]));
  110. renderWithQueryClient(
  111. <FileSelector value={1} onChange={() => {}} showPreview={true} />
  112. );
  113. await waitFor(() => {
  114. expect(screen.getByText('更换文件')).toBeInTheDocument();
  115. });
  116. });
  117. it('应该支持多选模式', async () => {
  118. const { fileClient } = await import('../../src/api/fileClient');
  119. (fileClient.index.$get as any).mockResolvedValue(createMockResponse(200, {
  120. data: mockFiles,
  121. pagination: { current: 1, pageSize: 50, total: 2 }
  122. }));
  123. const onChange = vi.fn();
  124. renderWithQueryClient(
  125. <FileSelector
  126. value={[1, 2]}
  127. onChange={onChange}
  128. allowMultiple={true}
  129. showPreview={true}
  130. />
  131. );
  132. await waitFor(() => {
  133. expect(screen.getByText('已选择 2 个文件')).toBeInTheDocument();
  134. });
  135. });
  136. it('应该过滤文件类型', async () => {
  137. const { fileClient } = await import('../../src/api/fileClient');
  138. (fileClient.index.$get as any).mockResolvedValue(createMockResponse(200, {
  139. data: mockFiles,
  140. pagination: { current: 1, pageSize: 50, total: 2 }
  141. }));
  142. renderWithQueryClient(
  143. <FileSelector
  144. value={null}
  145. onChange={() => {}}
  146. filterType="image"
  147. />
  148. );
  149. const selectButton = screen.getByTestId('file-selector-button');
  150. fireEvent.click(selectButton);
  151. await waitFor(() => {
  152. expect(fileClient.index.$get).toHaveBeenCalledWith({
  153. query: {
  154. page: 1,
  155. pageSize: 50,
  156. keyword: 'image'
  157. }
  158. });
  159. });
  160. });
  161. it('应该处理文件选择确认', async () => {
  162. const { fileClient } = await import('../../src/api/fileClient');
  163. (fileClient.index.$get as any).mockResolvedValue(createMockResponse(200, {
  164. data: mockFiles,
  165. pagination: { current: 1, pageSize: 50, total: 2 }
  166. }));
  167. const onChange = vi.fn();
  168. renderWithQueryClient(
  169. <FileSelector value={null} onChange={onChange} />
  170. );
  171. const selectButton = screen.getByTestId('file-selector-button');
  172. fireEvent.click(selectButton);
  173. await waitFor(() => {
  174. const fileItems = screen.getAllByText('test-image.jpg');
  175. fireEvent.click(fileItems[0]);
  176. });
  177. const confirmButton = screen.getByText('确认选择');
  178. fireEvent.click(confirmButton);
  179. expect(onChange).toHaveBeenCalledWith(1);
  180. });
  181. });