area-select.integration.test.tsx 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403
  1. import { describe, it, expect, vi, beforeEach } from 'vitest';
  2. import { render, screen, fireEvent, waitFor } from '@testing-library/react';
  3. import userEvent from '@testing-library/user-event';
  4. import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
  5. import { AreaSelect } from '../../src/components/AreaSelect';
  6. // Mock API 调用
  7. vi.mock('../../src/api/areaClient', () => ({
  8. areaClientManager: {
  9. get: vi.fn(() => ({
  10. index: {
  11. $get: vi.fn(async ({ query }) => {
  12. const filters = JSON.parse(query.filters);
  13. if (filters.level === 1) {
  14. // 省份数据
  15. return {
  16. status: 200,
  17. json: async () => ({
  18. data: [
  19. { id: 1, name: '北京市', level: 1, parentId: null },
  20. { id: 2, name: '上海市', level: 1, parentId: null }
  21. ]
  22. })
  23. };
  24. } else if (filters.level === 2 && filters.parentId === 1) {
  25. // 北京市的城市数据
  26. return {
  27. status: 200,
  28. json: async () => ({
  29. data: [
  30. { id: 3, name: '北京市', level: 2, parentId: 1 }
  31. ]
  32. })
  33. };
  34. } else if (filters.level === 2 && filters.parentId === 2) {
  35. // 上海市的城市数据
  36. return {
  37. status: 200,
  38. json: async () => ({
  39. data: [
  40. { id: 4, name: '上海市', level: 2, parentId: 2 }
  41. ]
  42. })
  43. };
  44. } else if (filters.level === 3 && filters.parentId === 3) {
  45. // 北京市的区县数据
  46. return {
  47. status: 200,
  48. json: async () => ({
  49. data: [
  50. { id: 5, name: '东城区', level: 3, parentId: 3 },
  51. { id: 6, name: '西城区', level: 3, parentId: 3 }
  52. ]
  53. })
  54. };
  55. } else if (filters.level === 3 && filters.parentId === 4) {
  56. // 上海市的区县数据
  57. return {
  58. status: 200,
  59. json: async () => ({
  60. data: [
  61. { id: 7, name: '黄浦区', level: 3, parentId: 4 },
  62. { id: 8, name: '徐汇区', level: 3, parentId: 4 }
  63. ]
  64. })
  65. };
  66. }
  67. return {
  68. status: 200,
  69. json: async () => ({ data: [] })
  70. };
  71. })
  72. }
  73. }))
  74. }
  75. }));
  76. // 创建测试用的 QueryClient
  77. const createTestQueryClient = () => new QueryClient({
  78. defaultOptions: {
  79. queries: {
  80. retry: false,
  81. },
  82. },
  83. });
  84. // 测试组件包装器
  85. const TestWrapper = ({ children }: { children: React.ReactNode }) => {
  86. return (
  87. <QueryClientProvider client={createTestQueryClient()}>
  88. {children}
  89. </QueryClientProvider>
  90. );
  91. };
  92. describe('AreaSelect 集成测试', () => {
  93. beforeEach(() => {
  94. vi.clearAllMocks();
  95. });
  96. it('应该正确渲染 AreaSelect 组件', async () => {
  97. render(
  98. <TestWrapper>
  99. <AreaSelect />
  100. </TestWrapper>
  101. );
  102. // 等待省份数据加载 - AreaSelect 显示的是"选择所在省份"
  103. await waitFor(() => {
  104. expect(screen.getByText('选择所在省份')).toBeInTheDocument();
  105. });
  106. expect(screen.getByText('省份')).toBeInTheDocument();
  107. expect(screen.getByText('城市')).toBeInTheDocument();
  108. expect(screen.getByText('区县')).toBeInTheDocument();
  109. });
  110. it('应该显示必填标记当 required=true 时', async () => {
  111. render(
  112. <TestWrapper>
  113. <AreaSelect required={true} />
  114. </TestWrapper>
  115. );
  116. // 等待省份数据加载 - AreaSelect 显示的是"选择所在省份"
  117. await waitFor(() => {
  118. expect(screen.getByText('选择所在省份')).toBeInTheDocument();
  119. });
  120. // 检查省份标签是否包含星号
  121. const provinceLabel = screen.getByText('省份');
  122. expect(provinceLabel.innerHTML).toContain('*');
  123. // 城市和区县不应该显示星号(初始状态)
  124. const cityLabel = screen.getByText('城市');
  125. expect(cityLabel.innerHTML).not.toContain('*');
  126. const districtLabel = screen.getByText('区县');
  127. expect(districtLabel.innerHTML).not.toContain('*');
  128. });
  129. it('应该正确处理省份选择并加载城市数据', async () => {
  130. const user = userEvent.setup();
  131. const handleChange = vi.fn();
  132. render(
  133. <TestWrapper>
  134. <AreaSelect onChange={handleChange} />
  135. </TestWrapper>
  136. );
  137. // 等待省份数据加载
  138. await waitFor(() => {
  139. expect(screen.getByText('选择所在省份')).toBeInTheDocument();
  140. });
  141. // 使用test ID查找省份选择框
  142. const provinceSelect = screen.getByTestId('area-select-province');
  143. await user.click(provinceSelect);
  144. // 等待下拉菜单出现并选择北京市
  145. await waitFor(() => {
  146. expect(screen.getByText('北京市')).toBeInTheDocument();
  147. });
  148. await user.click(screen.getByText('北京市'));
  149. // 验证onChange被调用
  150. expect(handleChange).toHaveBeenCalledWith({
  151. provinceId: 1,
  152. cityId: undefined,
  153. districtId: undefined
  154. });
  155. // 验证城市选择框应该被启用(因为选择了省份)
  156. const citySelect = screen.getByTestId('area-select-city');
  157. expect(citySelect).toBeInTheDocument();
  158. });
  159. it('应该正确处理城市选择并加载区县数据', async () => {
  160. const user = userEvent.setup();
  161. const handleChange = vi.fn();
  162. render(
  163. <TestWrapper>
  164. <AreaSelect onChange={handleChange} />
  165. </TestWrapper>
  166. );
  167. // 等待省份数据加载
  168. await waitFor(() => {
  169. expect(screen.getByText('选择所在省份')).toBeInTheDocument();
  170. });
  171. // 先选择省份(北京市)
  172. const provinceSelect = screen.getByTestId('area-select-province');
  173. await user.click(provinceSelect);
  174. await waitFor(() => {
  175. expect(screen.getByText('北京市')).toBeInTheDocument();
  176. });
  177. await user.click(screen.getByText('北京市'));
  178. // 等待城市数据加载
  179. await waitFor(() => {
  180. expect(screen.getByText('选择所在城市')).toBeInTheDocument();
  181. });
  182. // 点击城市选择框
  183. const citySelect = screen.getByTestId('area-select-city');
  184. await user.click(citySelect);
  185. // 选择北京市(城市)
  186. await waitFor(() => {
  187. expect(screen.getByText('北京市')).toBeInTheDocument();
  188. });
  189. await user.click(screen.getByText('北京市'));
  190. // 验证onChange被调用
  191. expect(handleChange).toHaveBeenCalledWith({
  192. provinceId: 1,
  193. cityId: 3,
  194. districtId: undefined
  195. });
  196. // 验证区县选择框应该被启用(因为选择了城市)
  197. const districtSelect = screen.getByTestId('area-select-district');
  198. expect(districtSelect).toBeInTheDocument();
  199. });
  200. it('应该正确处理区县选择', async () => {
  201. const user = userEvent.setup();
  202. const handleChange = vi.fn();
  203. render(
  204. <TestWrapper>
  205. <AreaSelect onChange={handleChange} />
  206. </TestWrapper>
  207. );
  208. // 等待省份数据加载
  209. await waitFor(() => {
  210. expect(screen.getByText('选择所在省份')).toBeInTheDocument();
  211. });
  212. // 先选择省份(北京市)
  213. const provinceSelect = screen.getByTestId('area-select-province');
  214. await user.click(provinceSelect);
  215. await waitFor(() => {
  216. expect(screen.getByText('北京市')).toBeInTheDocument();
  217. });
  218. await user.click(screen.getByText('北京市'));
  219. // 等待城市数据加载
  220. await waitFor(() => {
  221. expect(screen.getByText('选择所在城市')).toBeInTheDocument();
  222. });
  223. // 选择城市(北京市)
  224. const citySelect = screen.getByTestId('area-select-city');
  225. await user.click(citySelect);
  226. await waitFor(() => {
  227. expect(screen.getByText('北京市')).toBeInTheDocument();
  228. });
  229. await user.click(screen.getByText('北京市'));
  230. // 等待区县数据加载
  231. await waitFor(() => {
  232. expect(screen.getByText('选择所在区县')).toBeInTheDocument();
  233. });
  234. // 点击区县选择框
  235. const districtSelect = screen.getByTestId('area-select-district');
  236. await user.click(districtSelect);
  237. // 选择东城区
  238. await waitFor(() => {
  239. expect(screen.getByText('东城区')).toBeInTheDocument();
  240. });
  241. await user.click(screen.getByText('东城区'));
  242. // 验证onChange被调用
  243. expect(handleChange).toHaveBeenCalledWith({
  244. provinceId: 1,
  245. cityId: 3,
  246. districtId: 5
  247. });
  248. });
  249. it('应该支持禁用状态', async () => {
  250. render(
  251. <TestWrapper>
  252. <AreaSelect disabled={true} />
  253. </TestWrapper>
  254. );
  255. // 等待省份数据加载 - AreaSelect 显示的是"选择所在省份"
  256. await waitFor(() => {
  257. expect(screen.getByText('选择所在省份')).toBeInTheDocument();
  258. });
  259. // 检查组件渲染正常
  260. expect(screen.getByText('省份')).toBeInTheDocument();
  261. expect(screen.getByText('城市')).toBeInTheDocument();
  262. expect(screen.getByText('区县')).toBeInTheDocument();
  263. });
  264. it('应该正确处理初始值', async () => {
  265. const initialValue = {
  266. provinceId: 1,
  267. cityId: 3,
  268. districtId: 5
  269. };
  270. render(
  271. <TestWrapper>
  272. <AreaSelect value={initialValue} />
  273. </TestWrapper>
  274. );
  275. // 等待省份数据加载 - AreaSelect 显示的是"选择所在省份"
  276. await waitFor(() => {
  277. expect(screen.getByText('选择所在省份')).toBeInTheDocument();
  278. });
  279. // 组件应该能正常渲染,初始值会在组件内部处理
  280. expect(screen.getByText('省份')).toBeInTheDocument();
  281. expect(screen.getByText('城市')).toBeInTheDocument();
  282. expect(screen.getByText('区县')).toBeInTheDocument();
  283. });
  284. it('当选择新省份时应该清空城市和区县选择', async () => {
  285. const user = userEvent.setup();
  286. const handleChange = vi.fn();
  287. render(
  288. <TestWrapper>
  289. <AreaSelect onChange={handleChange} />
  290. </TestWrapper>
  291. );
  292. // 等待省份数据加载
  293. await waitFor(() => {
  294. expect(screen.getByText('选择所在省份')).toBeInTheDocument();
  295. });
  296. // 先选择省份(北京市)
  297. const provinceSelect = screen.getByTestId('area-select-province');
  298. await user.click(provinceSelect);
  299. await waitFor(() => {
  300. expect(screen.getByText('北京市')).toBeInTheDocument();
  301. });
  302. await user.click(screen.getByText('北京市'));
  303. // 等待城市数据加载并选择城市
  304. await waitFor(() => {
  305. expect(screen.getByText('选择所在城市')).toBeInTheDocument();
  306. });
  307. const citySelect = screen.getByTestId('area-select-city');
  308. await user.click(citySelect);
  309. await waitFor(() => {
  310. expect(screen.getByText('北京市')).toBeInTheDocument();
  311. });
  312. await user.click(screen.getByText('北京市'));
  313. // 等待区县数据加载并选择区县
  314. await waitFor(() => {
  315. expect(screen.getByText('选择所在区县')).toBeInTheDocument();
  316. });
  317. const districtSelect = screen.getByTestId('area-select-district');
  318. await user.click(districtSelect);
  319. await waitFor(() => {
  320. expect(screen.getByText('东城区')).toBeInTheDocument();
  321. });
  322. await user.click(screen.getByText('东城区'));
  323. // 验证已经选择了完整的省市区
  324. expect(handleChange).toHaveBeenLastCalledWith({
  325. provinceId: 1,
  326. cityId: 3,
  327. districtId: 5
  328. });
  329. // 现在选择新的省份(上海市)
  330. await user.click(screen.getByTestId('area-select-province')); // 重新打开省份选择
  331. await waitFor(() => {
  332. expect(screen.getByText('上海市')).toBeInTheDocument();
  333. });
  334. await user.click(screen.getByText('上海市'));
  335. // 验证onChange被调用,城市和区县被清空
  336. expect(handleChange).toHaveBeenLastCalledWith({
  337. provinceId: 2,
  338. cityId: undefined,
  339. districtId: undefined
  340. });
  341. // 验证城市选择框应该显示"选择城市"(被清空)
  342. expect(screen.getByTestId('area-select-city')).toBeInTheDocument();
  343. // 验证区县选择框应该显示"选择区县"(被清空)
  344. expect(screen.getByTestId('area-select-district')).toBeInTheDocument();
  345. });
  346. });