2
0

disabled-person-selector.integration.test.tsx 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  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 DisabledPersonSelector from '../../src/components/DisabledPersonSelector';
  5. import { disabilityClientManager } from '../../src/api/disabilityClient';
  6. import type { DisabledPersonData } from '../../src/api/types';
  7. // 完整的mock响应对象
  8. const createMockResponse = (status: number, data?: any) => ({
  9. status,
  10. ok: status >= 200 && status < 300,
  11. body: null,
  12. bodyUsed: false,
  13. statusText: status === 200 ? 'OK' : status === 201 ? 'Created' : status === 204 ? 'No Content' : 'Error',
  14. headers: new Headers(),
  15. url: '',
  16. redirected: false,
  17. type: 'basic' as ResponseType,
  18. json: async () => data || {},
  19. text: async () => '',
  20. blob: async () => new Blob(),
  21. arrayBuffer: async () => new ArrayBuffer(0),
  22. formData: async () => new FormData(),
  23. clone: function() { return this; }
  24. });
  25. // Mock API client
  26. vi.mock('../../src/api/disabilityClient', () => {
  27. const mockDisabilityClient = {
  28. searchDisabledPersons: {
  29. $get: vi.fn(() => Promise.resolve(createMockResponse(200, {
  30. data: [
  31. {
  32. id: 1,
  33. name: '张三',
  34. gender: '男',
  35. idCard: '110101199001011234',
  36. disabilityId: 'CJZ20240001',
  37. disabilityType: '视力残疾',
  38. disabilityLevel: '一级',
  39. idAddress: '北京市东城区',
  40. phone: '13800138000',
  41. province: '北京市',
  42. city: '北京市',
  43. district: '东城区',
  44. isInBlackList: 0,
  45. createTime: '2024-01-01T00:00:00Z',
  46. updateTime: '2024-01-01T00:00:00Z'
  47. },
  48. {
  49. id: 2,
  50. name: '李四',
  51. gender: '女',
  52. idCard: '110101199001011235',
  53. disabilityId: 'CJZ20240002',
  54. disabilityType: '听力残疾',
  55. disabilityLevel: '二级',
  56. idAddress: '上海市黄浦区',
  57. phone: '13800138001',
  58. province: '上海市',
  59. city: '上海市',
  60. district: '黄浦区',
  61. isInBlackList: 1, // 黑名单人员
  62. createTime: '2024-01-01T00:00:00Z',
  63. updateTime: '2024-01-01T00:00:00Z'
  64. }
  65. ],
  66. total: 2
  67. })))
  68. },
  69. getAllDisabledPersons: {
  70. $get: vi.fn(() => Promise.resolve(createMockResponse(200, {
  71. data: [
  72. {
  73. id: 3,
  74. name: '王五',
  75. gender: '男',
  76. idCard: '110101199001011236',
  77. disabilityId: 'CJZ20240003',
  78. disabilityType: '肢体残疾',
  79. disabilityLevel: '三级',
  80. idAddress: '广州市天河区',
  81. phone: '13800138002',
  82. province: '广东省',
  83. city: '广州市',
  84. district: '天河区',
  85. isInBlackList: 0,
  86. createTime: '2024-01-01T00:00:00Z',
  87. updateTime: '2024-01-01T00:00:00Z'
  88. }
  89. ],
  90. total: 1
  91. })))
  92. }
  93. };
  94. const mockClientManager = {
  95. get: vi.fn(() => mockDisabilityClient),
  96. init: vi.fn(() => mockDisabilityClient),
  97. reset: vi.fn(),
  98. getInstance: vi.fn(() => mockClientManager)
  99. };
  100. return {
  101. disabilityClientManager: mockClientManager,
  102. disabilityClient: mockDisabilityClient
  103. };
  104. });
  105. // Mock AreaSelect组件(与现有测试保持一致)
  106. vi.mock('@d8d/area-management-ui', () => ({
  107. AreaSelect: ({ onChange, value, disabled, 'data-testid': testId }: any) => (
  108. <div data-testid={testId || 'area-select'}>
  109. <select
  110. data-testid="area-province-select"
  111. onChange={(e) => onChange && onChange({ provinceId: e.target.value ? 1 : undefined })}
  112. value={value?.provinceId || ''}
  113. disabled={disabled}
  114. >
  115. <option value="">选择省份</option>
  116. <option value="1">北京市</option>
  117. </select>
  118. <select
  119. data-testid="area-city-select"
  120. onChange={(e) => onChange && onChange({ provinceId: 1, cityId: e.target.value ? 2 : undefined })}
  121. value={value?.cityId || ''}
  122. disabled={disabled}
  123. >
  124. <option value="">选择城市</option>
  125. <option value="2">北京市</option>
  126. </select>
  127. <select
  128. data-testid="area-district-select"
  129. onChange={(e) => onChange && onChange({ provinceId: 1, cityId: 2, districtId: e.target.value ? 3 : undefined })}
  130. value={value?.districtId || ''}
  131. disabled={disabled}
  132. >
  133. <option value="">选择区县</option>
  134. <option value="3">东城区</option>
  135. </select>
  136. </div>
  137. ),
  138. }));
  139. describe('DisabledPersonSelector', () => {
  140. let queryClient: QueryClient;
  141. let onOpenChange: ReturnType<typeof vi.fn>;
  142. let onSelect: ReturnType<typeof vi.fn>;
  143. beforeEach(() => {
  144. queryClient = new QueryClient({
  145. defaultOptions: {
  146. queries: {
  147. retry: false,
  148. },
  149. },
  150. });
  151. onOpenChange = vi.fn();
  152. onSelect = vi.fn();
  153. vi.clearAllMocks();
  154. });
  155. const renderComponent = (props = {}) => {
  156. return render(
  157. <QueryClientProvider client={queryClient}>
  158. <DisabledPersonSelector
  159. open={true}
  160. onOpenChange={onOpenChange}
  161. onSelect={onSelect}
  162. {...props}
  163. />
  164. </QueryClientProvider>
  165. );
  166. };
  167. it('应该渲染对话框和搜索区域', async () => {
  168. renderComponent();
  169. // 等待数据加载
  170. await waitFor(() => {
  171. expect(screen.getByText('选择残疾人')).toBeInTheDocument();
  172. });
  173. // 检查搜索字段
  174. expect(screen.getByTestId('search-name-input')).toBeInTheDocument();
  175. expect(screen.getByTestId('area-select')).toBeInTheDocument();
  176. expect(screen.getByText('搜索')).toBeInTheDocument();
  177. expect(screen.getByText('重置')).toBeInTheDocument();
  178. });
  179. it('应该显示残疾人列表', async () => {
  180. renderComponent();
  181. // 等待数据加载 - 应该调用 getAllDisabledPersons 因为没有搜索关键词
  182. await waitFor(() => {
  183. expect(disabilityClientManager.get().getAllDisabledPersons.$get).toHaveBeenCalled();
  184. });
  185. // 检查表格数据
  186. await waitFor(() => {
  187. expect(screen.getByText('王五')).toBeInTheDocument();
  188. });
  189. });
  190. it('应该处理搜索功能', async () => {
  191. renderComponent();
  192. // 输入搜索关键词
  193. const searchInput = screen.getByTestId('search-name-input');
  194. fireEvent.change(searchInput, { target: { value: '张三' } });
  195. // 点击搜索按钮
  196. const searchButton = screen.getByText('搜索');
  197. fireEvent.click(searchButton);
  198. // 验证搜索API被调用
  199. await waitFor(() => {
  200. expect(disabilityClientManager.get().searchDisabledPersons.$get).toHaveBeenCalledWith({
  201. query: {
  202. keyword: '张三',
  203. skip: 0,
  204. take: 10
  205. }
  206. });
  207. });
  208. });
  209. it('应该处理重置搜索', async () => {
  210. renderComponent();
  211. // 等待组件渲染
  212. await waitFor(() => {
  213. expect(screen.getByTestId('search-name-input')).toBeInTheDocument();
  214. });
  215. // 输入搜索关键词
  216. const searchInput = screen.getByTestId('search-name-input');
  217. fireEvent.change(searchInput, { target: { value: '张三' } });
  218. // 点击重置按钮
  219. const resetButton = screen.getByText('重置');
  220. fireEvent.click(resetButton);
  221. // 验证搜索输入被清空
  222. expect(searchInput).toHaveValue('');
  223. });
  224. it('应该处理单选模式', async () => {
  225. renderComponent({ mode: 'single' });
  226. // 等待数据加载
  227. await waitFor(() => {
  228. expect(disabilityClientManager.get().getAllDisabledPersons.$get).toHaveBeenCalled();
  229. });
  230. // 等待表格数据渲染
  231. await waitFor(() => {
  232. expect(screen.getByText('王五')).toBeInTheDocument();
  233. });
  234. // 点击表格行选择人员
  235. const firstRow = screen.getByText('王五').closest('tr');
  236. expect(firstRow).toBeInTheDocument();
  237. if (firstRow) {
  238. fireEvent.click(firstRow);
  239. }
  240. // 验证选择回调被调用
  241. expect(onSelect).toHaveBeenCalledWith(expect.objectContaining({
  242. id: 3,
  243. name: '王五'
  244. }));
  245. expect(onOpenChange).toHaveBeenCalledWith(false);
  246. });
  247. it('应该处理多选模式', async () => {
  248. renderComponent({ mode: 'multiple' });
  249. // 等待数据加载
  250. await waitFor(() => {
  251. expect(disabilityClientManager.get().getAllDisabledPersons.$get).toHaveBeenCalled();
  252. });
  253. // 等待表格数据渲染
  254. await waitFor(() => {
  255. expect(screen.getByText('王五')).toBeInTheDocument();
  256. });
  257. // 应该显示多选相关的UI
  258. expect(screen.getByText('已选择 0 人')).toBeInTheDocument();
  259. });
  260. it('应该处理对话框关闭', async () => {
  261. renderComponent();
  262. await waitFor(() => {
  263. expect(screen.getByText('取消')).toBeInTheDocument();
  264. });
  265. // 点击取消按钮
  266. const cancelButton = screen.getByText('取消');
  267. fireEvent.click(cancelButton);
  268. expect(onOpenChange).toHaveBeenCalledWith(false);
  269. });
  270. });