userManagement.integration.test.tsx 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  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 { UserManagement } from '../../src/components/UserManagement';
  5. // Mock API client
  6. vi.mock('../../src/api/userClient', () => ({
  7. userClient: {
  8. index: {
  9. $get: vi.fn(),
  10. $post: vi.fn(),
  11. },
  12. ':id': {
  13. $put: vi.fn(),
  14. $delete: vi.fn(),
  15. },
  16. },
  17. }));
  18. // Mock toast
  19. vi.mock('sonner', () => ({
  20. toast: {
  21. success: vi.fn(),
  22. error: vi.fn(),
  23. },
  24. }));
  25. const createTestQueryClient = () =>
  26. new QueryClient({
  27. defaultOptions: {
  28. queries: {
  29. retry: false,
  30. },
  31. },
  32. });
  33. const renderWithProviders = (component: React.ReactNode) => {
  34. const queryClient = createTestQueryClient();
  35. return render(
  36. <QueryClientProvider client={queryClient}>
  37. {component as any}
  38. </QueryClientProvider>
  39. );
  40. };
  41. describe('UserManagement Integration Tests', () => {
  42. beforeEach(() => {
  43. vi.clearAllMocks();
  44. });
  45. it('should complete full user CRUD flow', async () => {
  46. const mockUsers = {
  47. data: [
  48. {
  49. id: 1,
  50. username: 'existinguser',
  51. nickname: 'Existing User',
  52. email: 'existing@example.com',
  53. phone: '1234567890',
  54. name: 'Existing Name',
  55. isDisabled: 0,
  56. createdAt: '2024-01-01T00:00:00Z',
  57. roles: [{ id: 1, name: 'admin' }],
  58. avatarFile: null,
  59. },
  60. ],
  61. pagination: {
  62. total: 1,
  63. page: 1,
  64. pageSize: 10,
  65. },
  66. };
  67. const { userClient } = await import('../../src/api/userClient');
  68. const { toast } = await import('sonner');
  69. // Mock initial user list
  70. (userClient.index.$get as any).mockResolvedValue({
  71. status: 200,
  72. json: async () => mockUsers,
  73. });
  74. renderWithProviders(<UserManagement />);
  75. // Wait for initial data to load
  76. await waitFor(() => {
  77. expect(screen.getByText('existinguser')).toBeInTheDocument();
  78. });
  79. // Test create user
  80. const createButton = screen.getByText('创建用户');
  81. fireEvent.click(createButton);
  82. // Fill create form
  83. const usernameInput = screen.getByPlaceholderText('请输入用户名');
  84. const passwordInput = screen.getByPlaceholderText('请输入密码');
  85. const emailInput = screen.getByPlaceholderText('请输入邮箱');
  86. fireEvent.change(usernameInput, { target: { value: 'newuser' } });
  87. fireEvent.change(passwordInput, { target: { value: 'password123' } });
  88. fireEvent.change(emailInput, { target: { value: 'new@example.com' } });
  89. // Mock successful creation
  90. (userClient.index.$post as any).mockResolvedValue({
  91. status: 201,
  92. });
  93. const submitButton = screen.getByText('创建用户');
  94. fireEvent.click(submitButton);
  95. await waitFor(() => {
  96. expect(userClient.index.$post).toHaveBeenCalledWith({
  97. json: {
  98. username: 'newuser',
  99. password: 'password123',
  100. email: 'new@example.com',
  101. nickname: undefined,
  102. phone: null,
  103. name: null,
  104. isDisabled: 0,
  105. },
  106. });
  107. expect(toast.success).toHaveBeenCalledWith('用户创建成功');
  108. });
  109. // Test edit user
  110. const editButtons = screen.getAllByRole('button', { name: /edit/i });
  111. fireEvent.click(editButtons[0]);
  112. // Verify edit form is populated
  113. await waitFor(() => {
  114. expect(screen.getByDisplayValue('existinguser')).toBeInTheDocument();
  115. });
  116. // Update user
  117. const updateUsernameInput = screen.getByDisplayValue('existinguser');
  118. fireEvent.change(updateUsernameInput, { target: { value: 'updateduser' } });
  119. // Mock successful update
  120. (userClient[':id']['$put'] as any).mockResolvedValue({
  121. status: 200,
  122. });
  123. const updateButton = screen.getByText('更新用户');
  124. fireEvent.click(updateButton);
  125. await waitFor(() => {
  126. expect(userClient[':id']['$put']).toHaveBeenCalledWith({
  127. param: { id: 1 },
  128. json: {
  129. username: 'updateduser',
  130. nickname: 'Existing User',
  131. email: 'existing@example.com',
  132. phone: '1234567890',
  133. name: 'Existing Name',
  134. password: undefined,
  135. avatarFileId: null,
  136. isDisabled: 0,
  137. },
  138. });
  139. expect(toast.success).toHaveBeenCalledWith('用户更新成功');
  140. });
  141. // Test delete user
  142. const deleteButtons = screen.getAllByRole('button', { name: /trash/i });
  143. fireEvent.click(deleteButtons[0]);
  144. // Confirm deletion
  145. expect(screen.getByText('确认删除')).toBeInTheDocument();
  146. // Mock successful deletion
  147. (userClient[':id']['$delete'] as any).mockResolvedValue({
  148. status: 204,
  149. });
  150. const confirmDeleteButton = screen.getByText('删除');
  151. fireEvent.click(confirmDeleteButton);
  152. await waitFor(() => {
  153. expect(userClient[':id']['$delete']).toHaveBeenCalledWith({
  154. param: { id: 1 },
  155. });
  156. expect(toast.success).toHaveBeenCalledWith('用户删除成功');
  157. });
  158. });
  159. it('should handle API errors gracefully', async () => {
  160. const { userClient } = await import('../../src/api/userClient');
  161. const { toast } = await import('sonner');
  162. // Mock API error
  163. (userClient.index.$get as any).mockRejectedValue(new Error('API Error'));
  164. renderWithProviders(<UserManagement />);
  165. // Should handle error without crashing
  166. await waitFor(() => {
  167. expect(screen.getByText('用户管理')).toBeInTheDocument();
  168. });
  169. // Test create user error
  170. const createButton = screen.getByText('创建用户');
  171. fireEvent.click(createButton);
  172. const usernameInput = screen.getByPlaceholderText('请输入用户名');
  173. const passwordInput = screen.getByPlaceholderText('请输入密码');
  174. fireEvent.change(usernameInput, { target: { value: 'testuser' } });
  175. fireEvent.change(passwordInput, { target: { value: 'password' } });
  176. // Mock creation error
  177. (userClient.index.$post as any).mockRejectedValue(new Error('Creation failed'));
  178. const submitButton = screen.getByText('创建用户');
  179. fireEvent.click(submitButton);
  180. await waitFor(() => {
  181. expect(toast.error).toHaveBeenCalledWith('创建失败,请重试');
  182. });
  183. });
  184. it('should handle search and filter integration', async () => {
  185. const { userClient } = await import('../../src/api/userClient');
  186. const mockUsers = {
  187. data: [],
  188. pagination: { total: 0, page: 1, pageSize: 10 },
  189. };
  190. (userClient.index.$get as any).mockResolvedValue({
  191. status: 200,
  192. json: async () => mockUsers,
  193. });
  194. renderWithProviders(<UserManagement />);
  195. // Test search
  196. const searchInput = screen.getByPlaceholderText('搜索用户名、昵称或邮箱...');
  197. fireEvent.change(searchInput, { target: { value: 'searchterm' } });
  198. await waitFor(() => {
  199. expect(userClient.index.$get).toHaveBeenCalledWith({
  200. query: {
  201. page: 1,
  202. pageSize: 10,
  203. keyword: 'searchterm',
  204. filters: undefined,
  205. },
  206. });
  207. });
  208. // Test filter
  209. const filterButton = screen.getByText('高级筛选');
  210. fireEvent.click(filterButton);
  211. // Apply status filter
  212. const statusSelect = screen.getByText('选择状态');
  213. fireEvent.click(statusSelect);
  214. const enabledOption = screen.getByText('启用');
  215. fireEvent.click(enabledOption);
  216. await waitFor(() => {
  217. expect(userClient.index.$get).toHaveBeenCalledWith({
  218. query: {
  219. page: 1,
  220. pageSize: 10,
  221. keyword: 'searchterm',
  222. filters: expect.stringContaining('isDisabled'),
  223. },
  224. });
  225. });
  226. });
  227. });