2
0

LocationSelect.test.tsx 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. import { describe, it, expect, vi, beforeEach } from 'vitest';
  2. import { render, screen, waitFor } from '@testing-library/react';
  3. import userEvent from '@testing-library/user-event';
  4. import '@testing-library/jest-dom';
  5. import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
  6. import { LocationSelect } from '@/client/admin/components/LocationSelect';
  7. // Mock API 客户端
  8. import { locationClient } from '@/client/api';
  9. // 创建测试包装器
  10. const TestWrapper = ({ children }: { children: React.ReactNode }) => {
  11. const queryClient = new QueryClient({
  12. defaultOptions: {
  13. queries: {
  14. retry: false,
  15. },
  16. },
  17. });
  18. return (
  19. <QueryClientProvider client={queryClient}>
  20. {children}
  21. </QueryClientProvider>
  22. );
  23. };
  24. // Mock API 客户端
  25. vi.mock('@/client/api', () => ({
  26. locationClient: {
  27. $get: vi.fn().mockResolvedValue({
  28. status: 200,
  29. ok: true,
  30. json: async () => ({
  31. data: [
  32. {
  33. id: 1,
  34. name: '北京天安门',
  35. address: '北京市东城区天安门广场',
  36. province: {
  37. id: 1,
  38. name: '北京市',
  39. code: '110000'
  40. },
  41. city: {
  42. id: 2,
  43. name: '北京市',
  44. code: '110100'
  45. },
  46. district: {
  47. id: 3,
  48. name: '东城区',
  49. code: '110101'
  50. },
  51. latitude: 39.9042,
  52. longitude: 116.4074,
  53. isDisabled: 0,
  54. createdAt: '2024-01-01T00:00:00.000Z',
  55. updatedAt: '2024-01-01T00:00:00.000Z'
  56. },
  57. {
  58. id: 2,
  59. name: '上海外滩',
  60. address: '上海市黄浦区外滩',
  61. province: {
  62. id: 4,
  63. name: '上海市',
  64. code: '310000'
  65. },
  66. city: {
  67. id: 5,
  68. name: '上海市',
  69. code: '310100'
  70. },
  71. district: {
  72. id: 6,
  73. name: '黄浦区',
  74. code: '310101'
  75. },
  76. latitude: 31.2304,
  77. longitude: 121.4737,
  78. isDisabled: 0,
  79. createdAt: '2024-01-01T00:00:00.000Z',
  80. updatedAt: '2024-01-01T00:00:00.000Z'
  81. }
  82. ],
  83. pagination: {
  84. current: 1,
  85. pageSize: 100,
  86. total: 2
  87. }
  88. })
  89. })
  90. }
  91. }));
  92. describe('LocationSelect', () => {
  93. const user = userEvent.setup();
  94. const mockOnValueChange = vi.fn();
  95. beforeEach(() => {
  96. vi.clearAllMocks();
  97. });
  98. it('应该正确渲染默认状态', () => {
  99. render(
  100. <TestWrapper>
  101. <LocationSelect
  102. value={undefined}
  103. onValueChange={mockOnValueChange}
  104. placeholder="选择地点..."
  105. />
  106. </TestWrapper>
  107. );
  108. expect(screen.getByRole('combobox')).toHaveTextContent('选择地点...');
  109. });
  110. it('应该显示选中的地点', async () => {
  111. render(
  112. <TestWrapper>
  113. <LocationSelect
  114. value={1}
  115. onValueChange={mockOnValueChange}
  116. placeholder="选择地点..."
  117. />
  118. </TestWrapper>
  119. );
  120. // 等待地点数据加载
  121. await waitFor(() => {
  122. expect(screen.getByText('北京天安门')).toBeInTheDocument();
  123. });
  124. });
  125. it('应该打开下拉菜单并显示地点列表', async () => {
  126. render(
  127. <TestWrapper>
  128. <LocationSelect
  129. value={undefined}
  130. onValueChange={mockOnValueChange}
  131. placeholder="选择地点..."
  132. />
  133. </TestWrapper>
  134. );
  135. // 点击打开下拉菜单
  136. await user.click(screen.getByRole('combobox'));
  137. // 等待地点数据加载
  138. await waitFor(() => {
  139. expect(screen.getByText('北京天安门')).toBeInTheDocument();
  140. expect(screen.getByText('上海外滩')).toBeInTheDocument();
  141. });
  142. // 验证地点信息显示
  143. expect(screen.getByText('北京市 / 北京市 / 东城区')).toBeInTheDocument();
  144. expect(screen.getByText('上海市 / 上海市 / 黄浦区')).toBeInTheDocument();
  145. });
  146. it('应该支持搜索地点', async () => {
  147. render(
  148. <TestWrapper>
  149. <LocationSelect
  150. value={undefined}
  151. onValueChange={mockOnValueChange}
  152. placeholder="选择地点..."
  153. />
  154. </TestWrapper>
  155. );
  156. // 点击打开下拉菜单
  157. await user.click(screen.getByRole('combobox'));
  158. // 等待地点数据加载
  159. await waitFor(() => {
  160. expect(screen.getByText('北京天安门')).toBeInTheDocument();
  161. });
  162. // 在搜索框中输入
  163. const searchInput = screen.getByPlaceholderText('搜索地点名称、地址或区域...');
  164. await user.type(searchInput, '上海');
  165. // 验证搜索请求被调用
  166. await waitFor(() => {
  167. expect(locationClient.$get).toHaveBeenCalledWith({
  168. query: {
  169. pageSize: 100,
  170. keyword: '上海',
  171. filters: JSON.stringify({ isDisabled: 0 })
  172. }
  173. });
  174. });
  175. });
  176. it('应该能够选择地点', async () => {
  177. render(
  178. <TestWrapper>
  179. <LocationSelect
  180. value={undefined}
  181. onValueChange={mockOnValueChange}
  182. placeholder="选择地点..."
  183. />
  184. </TestWrapper>
  185. );
  186. // 点击打开下拉菜单
  187. await user.click(screen.getByRole('combobox'));
  188. // 等待地点数据加载
  189. await waitFor(() => {
  190. expect(screen.getByText('北京天安门')).toBeInTheDocument();
  191. });
  192. // 选择第一个地点
  193. await user.click(screen.getByText('北京天安门'));
  194. // 验证回调被调用
  195. expect(mockOnValueChange).toHaveBeenCalledWith(1);
  196. });
  197. it('应该能够清除选择', async () => {
  198. render(
  199. <TestWrapper>
  200. <LocationSelect
  201. value={1}
  202. onValueChange={mockOnValueChange}
  203. placeholder="选择地点..."
  204. />
  205. </TestWrapper>
  206. );
  207. // 等待选中地点显示
  208. await waitFor(() => {
  209. expect(screen.getByText('北京天安门')).toBeInTheDocument();
  210. });
  211. // 点击打开下拉菜单
  212. await user.click(screen.getByRole('combobox'));
  213. // 在搜索框中输入以显示清除选项
  214. const searchInput = screen.getByPlaceholderText('搜索地点名称、地址或区域...');
  215. await user.type(searchInput, 'test');
  216. // 点击清除选择
  217. await waitFor(() => {
  218. const clearOption = screen.getByText('清除选择');
  219. expect(clearOption).toBeInTheDocument();
  220. });
  221. await user.click(screen.getByText('清除选择'));
  222. // 验证回调被调用
  223. expect(mockOnValueChange).toHaveBeenCalledWith(undefined);
  224. });
  225. it('应该显示加载状态', async () => {
  226. // 模拟加载延迟
  227. vi.mocked(locationClient.$get).mockImplementationOnce(() =>
  228. new Promise(resolve => setTimeout(() => resolve({
  229. status: 200,
  230. ok: true,
  231. json: async () => ({
  232. data: [],
  233. pagination: { current: 1, pageSize: 100, total: 0 }
  234. })
  235. } as any), 100))
  236. );
  237. render(
  238. <TestWrapper>
  239. <LocationSelect
  240. value={undefined}
  241. onValueChange={mockOnValueChange}
  242. placeholder="选择地点..."
  243. />
  244. </TestWrapper>
  245. );
  246. // 点击打开下拉菜单
  247. await user.click(screen.getByRole('combobox'));
  248. // 验证加载中状态
  249. expect(screen.getByText('加载中...')).toBeInTheDocument();
  250. });
  251. it('应该显示禁用状态', () => {
  252. render(
  253. <TestWrapper>
  254. <LocationSelect
  255. value={undefined}
  256. onValueChange={mockOnValueChange}
  257. placeholder="选择地点..."
  258. disabled={true}
  259. />
  260. </TestWrapper>
  261. );
  262. expect(screen.getByRole('combobox')).toBeDisabled();
  263. });
  264. });