userManagement.integration.test.tsx 7.5 KB

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