2
0

LocationSearch.test.tsx 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. import React from 'react'
  2. import { render, fireEvent, waitFor } from '@testing-library/react'
  3. import { LocationSearch } from '../../src/components/LocationSearch'
  4. // Mock API调用
  5. jest.mock('../../src/api', () => ({
  6. locationClient: {
  7. $get: jest.fn().mockImplementation(({ query }) => {
  8. if (query.keyword === '北京') {
  9. return Promise.resolve({
  10. status: 200,
  11. json: jest.fn().mockResolvedValue([
  12. {
  13. id: 1,
  14. name: '北京首都国际机场',
  15. province: '北京市',
  16. city: '北京市',
  17. district: '朝阳区',
  18. address: '北京市朝阳区首都机场路'
  19. },
  20. {
  21. id: 2,
  22. name: '北京南站',
  23. province: '北京市',
  24. city: '北京市',
  25. district: '丰台区',
  26. address: '北京市丰台区永外大街12号'
  27. }
  28. ])
  29. })
  30. }
  31. if (query.keyword === '上海') {
  32. return Promise.resolve({
  33. status: 200,
  34. json: jest.fn().mockResolvedValue([
  35. {
  36. id: 3,
  37. name: '上海虹桥机场',
  38. province: '上海市',
  39. city: '上海市',
  40. district: '长宁区',
  41. address: '上海市长宁区虹桥路2550号'
  42. }
  43. ])
  44. })
  45. }
  46. return Promise.resolve({
  47. status: 200,
  48. json: jest.fn().mockResolvedValue([])
  49. })
  50. })
  51. }
  52. }))
  53. describe('LocationSearch', () => {
  54. beforeEach(() => {
  55. jest.clearAllMocks()
  56. })
  57. it('应该正确渲染初始状态', () => {
  58. const { getByPlaceholderText } = render(<LocationSearch />)
  59. expect(getByPlaceholderText('搜索地点')).toBeTruthy()
  60. })
  61. it('应该显示自定义占位符', () => {
  62. const { getByPlaceholderText } = render(
  63. <LocationSearch placeholder="请输入地点名称" />
  64. )
  65. expect(getByPlaceholderText('请输入地点名称')).toBeTruthy()
  66. })
  67. it('应该处理输入变化并显示搜索结果', async () => {
  68. const { getByPlaceholderText, getByText } = render(<LocationSearch />)
  69. const input = getByPlaceholderText('搜索地点')
  70. fireEvent.input(input, { target: { value: '北京' } })
  71. await waitFor(() => {
  72. expect(getByText('北京首都国际机场')).toBeTruthy()
  73. expect(getByText('北京南站')).toBeTruthy()
  74. })
  75. })
  76. it('应该正确选择地点并触发onChange', async () => {
  77. const mockOnChange = jest.fn()
  78. const { getByPlaceholderText, getByText } = render(
  79. <LocationSearch onChange={mockOnChange} />
  80. )
  81. const input = getByPlaceholderText('搜索地点')
  82. fireEvent.input(input, { target: { value: '北京' } })
  83. await waitFor(() => {
  84. expect(getByText('北京首都国际机场')).toBeTruthy()
  85. })
  86. const locationItem = getByText('北京首都国际机场')
  87. fireEvent.click(locationItem)
  88. expect(mockOnChange).toHaveBeenCalledWith({
  89. id: 1,
  90. name: '北京首都国际机场',
  91. province: '北京市',
  92. city: '北京市',
  93. district: '朝阳区',
  94. address: '北京市朝阳区首都机场路'
  95. })
  96. })
  97. it('应该支持地区筛选', async () => {
  98. const { getByPlaceholderText } = render(
  99. <LocationSearch
  100. areaFilter={{
  101. provinceId: 1,
  102. cityId: 11,
  103. districtId: 111
  104. }}
  105. />
  106. )
  107. const input = getByPlaceholderText('搜索地点')
  108. fireEvent.input(input, { target: { value: '北京' } })
  109. await waitFor(() => {
  110. // 验证API调用时传递了地区筛选参数
  111. const locationClient = require('../../src/api').locationClient
  112. expect(locationClient.$get).toHaveBeenCalledWith({
  113. query: {
  114. keyword: '北京',
  115. provinceId: 1,
  116. cityId: 11,
  117. districtId: 111
  118. }
  119. })
  120. })
  121. })
  122. it('应该处理清除操作', async () => {
  123. const mockOnChange = jest.fn()
  124. const { getByPlaceholderText, getByText } = render(
  125. <LocationSearch onChange={mockOnChange} />
  126. )
  127. const input = getByPlaceholderText('搜索地点')
  128. fireEvent.input(input, { target: { value: '北京' } })
  129. await waitFor(() => {
  130. expect(getByText('北京首都国际机场')).toBeTruthy()
  131. })
  132. // 选择地点
  133. const locationItem = getByText('北京首都国际机场')
  134. fireEvent.click(locationItem)
  135. // 清除输入
  136. const clearButton = getByText('×')
  137. fireEvent.click(clearButton)
  138. expect(mockOnChange).toHaveBeenCalledWith(null)
  139. })
  140. it('应该显示当前选择的地点', () => {
  141. const selectedLocation = {
  142. id: 1,
  143. name: '北京首都国际机场',
  144. province: '北京市',
  145. city: '北京市',
  146. district: '朝阳区',
  147. address: '北京市朝阳区首都机场路'
  148. }
  149. const { getByText } = render(
  150. <LocationSearch value={selectedLocation} />
  151. )
  152. expect(getByText('已选择: 北京首都国际机场 · 朝阳区 · 北京市 · 北京市')).toBeTruthy()
  153. })
  154. it('应该处理空搜索结果', async () => {
  155. const { getByPlaceholderText, getByText } = render(<LocationSearch />)
  156. const input = getByPlaceholderText('搜索地点')
  157. fireEvent.input(input, { target: { value: '不存在的城市' } })
  158. await waitFor(() => {
  159. expect(getByText('未找到相关地点')).toBeTruthy()
  160. })
  161. })
  162. it('应该处理防抖搜索', async () => {
  163. jest.useFakeTimers()
  164. const { getByPlaceholderText } = render(<LocationSearch />)
  165. const input = getByPlaceholderText('搜索地点')
  166. // 快速输入多个字符
  167. fireEvent.input(input, { target: { value: '北' } })
  168. fireEvent.input(input, { target: { value: '北京' } })
  169. fireEvent.input(input, { target: { value: '北京市' } })
  170. // 验证API只被调用一次(防抖)
  171. const locationClient = require('../../src/api').locationClient
  172. expect(locationClient.$get).not.toHaveBeenCalled()
  173. // 快进防抖时间
  174. jest.advanceTimersByTime(300)
  175. await waitFor(() => {
  176. expect(locationClient.$get).toHaveBeenCalledTimes(1)
  177. expect(locationClient.$get).toHaveBeenCalledWith({
  178. query: {
  179. keyword: '北京市'
  180. }
  181. })
  182. })
  183. jest.useRealTimers()
  184. })
  185. })