index.spec.ts 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. import { describe, it, expect, vi, beforeEach } from 'vitest'
  2. import { mount, flushPromises } from '@vue/test-utils'
  3. import { createPinia, setActivePinia } from 'pinia'
  4. import CameraView from '@/views/camera/index.vue'
  5. import { wrapResponse, mockCameras, mockMachines } from '../../../fixtures'
  6. // Mock vue-router
  7. const mockPush = vi.fn()
  8. vi.mock('vue-router', () => ({
  9. useRouter: () => ({ push: mockPush }),
  10. useRoute: () => ({ query: {} })
  11. }))
  12. // Mock element-plus
  13. vi.mock('element-plus', () => ({
  14. ElMessage: {
  15. success: vi.fn(),
  16. error: vi.fn(),
  17. info: vi.fn(),
  18. warning: vi.fn()
  19. },
  20. ElMessageBox: {
  21. confirm: vi.fn().mockResolvedValue(true)
  22. }
  23. }))
  24. // Mock camera API
  25. const mockAdminListCameras = vi.fn()
  26. const mockAdminAddCamera = vi.fn()
  27. const mockAdminUpdateCamera = vi.fn()
  28. const mockAdminDeleteCamera = vi.fn()
  29. const mockAdminCheckCamera = vi.fn()
  30. vi.mock('@/api/camera', () => ({
  31. adminListCameras: (...args: any[]) => mockAdminListCameras(...args),
  32. adminAddCamera: (...args: any[]) => mockAdminAddCamera(...args),
  33. adminUpdateCamera: (...args: any[]) => mockAdminUpdateCamera(...args),
  34. adminDeleteCamera: (...args: any[]) => mockAdminDeleteCamera(...args),
  35. adminCheckCamera: (...args: any[]) => mockAdminCheckCamera(...args)
  36. }))
  37. // Mock machine API
  38. const mockListMachines = vi.fn()
  39. vi.mock('@/api/machine', () => ({
  40. listMachines: () => mockListMachines()
  41. }))
  42. describe('Camera View', () => {
  43. beforeEach(() => {
  44. setActivePinia(createPinia())
  45. vi.clearAllMocks()
  46. mockAdminListCameras.mockResolvedValue(wrapResponse(mockCameras))
  47. mockListMachines.mockResolvedValue(wrapResponse(mockMachines))
  48. })
  49. const mountCamera = () => {
  50. return mount(CameraView, {
  51. global: {
  52. plugins: [createPinia()],
  53. stubs: {
  54. 'el-form': { template: '<form><slot /></form>' },
  55. 'el-form-item': { template: '<div class="el-form-item"><slot /></div>' },
  56. 'el-input': {
  57. template: '<input :value="modelValue" @input="$emit(\'update:modelValue\', $event.target.value)" />',
  58. props: ['modelValue', 'placeholder', 'disabled', 'type']
  59. },
  60. 'el-input-number': {
  61. template:
  62. '<input type="number" :value="modelValue" @input="$emit(\'update:modelValue\', parseInt($event.target.value))" />',
  63. props: ['modelValue', 'min', 'max']
  64. },
  65. 'el-select': {
  66. template:
  67. '<select :value="modelValue" @change="$emit(\'update:modelValue\', $event.target.value); $emit(\'change\', $event.target.value)"><slot /></select>',
  68. props: ['modelValue', 'placeholder', 'clearable']
  69. },
  70. 'el-option': {
  71. template: '<option :value="value">{{ label }}</option>',
  72. props: ['label', 'value']
  73. },
  74. 'el-button': {
  75. template: '<button :type="htmlType" :disabled="loading" @click="$emit(\'click\')"><slot /></button>',
  76. props: ['type', 'icon', 'loading', 'link', 'plain'],
  77. computed: {
  78. htmlType() {
  79. return 'button'
  80. }
  81. }
  82. },
  83. 'el-table': {
  84. template: '<table class="el-table"><slot /></table>',
  85. props: ['data', 'loading', 'border']
  86. },
  87. 'el-table-column': {
  88. template: '<td class="el-table-column"></td>',
  89. props: ['prop', 'label', 'width', 'align', 'type', 'fixed', 'minWidth', 'showOverflowTooltip']
  90. },
  91. 'el-tag': {
  92. template: '<span class="el-tag"><slot /></span>',
  93. props: ['type']
  94. },
  95. 'el-dialog': {
  96. template: '<div v-if="modelValue" class="el-dialog"><slot /><slot name="footer" /></div>',
  97. props: ['modelValue', 'title', 'width', 'destroyOnClose']
  98. },
  99. 'el-row': { template: '<div class="el-row"><slot /></div>' },
  100. 'el-col': { template: '<div class="el-col"><slot /></div>', props: ['span'] },
  101. 'el-switch': {
  102. template:
  103. '<input type="checkbox" :checked="modelValue" @change="$emit(\'update:modelValue\', $event.target.checked)" />',
  104. props: ['modelValue']
  105. }
  106. }
  107. }
  108. })
  109. }
  110. describe('页面渲染', () => {
  111. it('应该正确渲染摄像头管理页面', async () => {
  112. const wrapper = mountCamera()
  113. await flushPromises()
  114. expect(wrapper.find('.page-container').exists()).toBe(true)
  115. expect(wrapper.find('.search-form').exists()).toBe(true)
  116. expect(wrapper.find('.table-actions').exists()).toBe(true)
  117. })
  118. it('应该显示新增摄像头按钮', async () => {
  119. const wrapper = mountCamera()
  120. await flushPromises()
  121. const addButton = wrapper.find('.table-actions button')
  122. expect(addButton.exists()).toBe(true)
  123. expect(addButton.text()).toContain('新增摄像头')
  124. })
  125. it('应该显示刷新列表按钮', async () => {
  126. const wrapper = mountCamera()
  127. await flushPromises()
  128. const buttons = wrapper.findAll('.table-actions button')
  129. const refreshBtn = buttons.find((b) => b.text().includes('刷新列表'))
  130. expect(refreshBtn).toBeDefined()
  131. })
  132. })
  133. describe('数据加载', () => {
  134. it('页面加载时应该获取摄像头列表', async () => {
  135. mountCamera()
  136. await flushPromises()
  137. expect(mockAdminListCameras).toHaveBeenCalled()
  138. })
  139. it('页面加载时应该获取机器列表', async () => {
  140. mountCamera()
  141. await flushPromises()
  142. expect(mockListMachines).toHaveBeenCalled()
  143. })
  144. })
  145. describe('搜索和过滤', () => {
  146. it('选择机器应该触发查询', async () => {
  147. const wrapper = mountCamera()
  148. await flushPromises()
  149. mockAdminListCameras.mockClear()
  150. const searchBtn = wrapper.findAll('button').find((btn) => btn.text().includes('搜索'))
  151. if (searchBtn) {
  152. await searchBtn.trigger('click')
  153. await flushPromises()
  154. expect(mockAdminListCameras).toHaveBeenCalled()
  155. }
  156. })
  157. it('重置应该清空筛选条件', async () => {
  158. const wrapper = mountCamera()
  159. await flushPromises()
  160. mockAdminListCameras.mockClear()
  161. const resetBtn = wrapper.findAll('button').find((btn) => btn.text().includes('重置'))
  162. if (resetBtn) {
  163. await resetBtn.trigger('click')
  164. await flushPromises()
  165. expect(mockAdminListCameras).toHaveBeenCalled()
  166. }
  167. })
  168. })
  169. describe('新增摄像头', () => {
  170. it('点击新增按钮应该打开弹窗', async () => {
  171. const wrapper = mountCamera()
  172. await flushPromises()
  173. const addBtn = wrapper.findAll('button').find((btn) => btn.text().includes('新增摄像头'))
  174. if (addBtn) {
  175. await addBtn.trigger('click')
  176. await flushPromises()
  177. expect(wrapper.find('.el-dialog').exists()).toBe(true)
  178. }
  179. })
  180. it('新增摄像头成功应该刷新列表', async () => {
  181. mockAdminAddCamera.mockResolvedValue(wrapResponse({ id: 4, cameraId: 'cam-004' }))
  182. const wrapper = mountCamera()
  183. await flushPromises()
  184. expect(mockAdminListCameras).toHaveBeenCalled()
  185. })
  186. })
  187. describe('编辑摄像头', () => {
  188. it('编辑摄像头成功应该刷新列表', async () => {
  189. mockAdminUpdateCamera.mockResolvedValue(wrapResponse(mockCameras[0]))
  190. const wrapper = mountCamera()
  191. await flushPromises()
  192. expect(mockAdminListCameras).toHaveBeenCalled()
  193. })
  194. })
  195. describe('删除摄像头', () => {
  196. it('删除摄像头成功应该刷新列表', async () => {
  197. mockAdminDeleteCamera.mockResolvedValue(wrapResponse(null))
  198. const wrapper = mountCamera()
  199. await flushPromises()
  200. expect(mockAdminListCameras).toHaveBeenCalled()
  201. })
  202. })
  203. describe('检测摄像头', () => {
  204. it('检测成功应该显示成功消息', async () => {
  205. mockAdminCheckCamera.mockResolvedValue(wrapResponse(true))
  206. const wrapper = mountCamera()
  207. await flushPromises()
  208. expect(mockAdminListCameras).toHaveBeenCalled()
  209. })
  210. it('检测失败应该显示警告消息', async () => {
  211. mockAdminCheckCamera.mockResolvedValue(wrapResponse(false))
  212. const wrapper = mountCamera()
  213. await flushPromises()
  214. expect(mockAdminListCameras).toHaveBeenCalled()
  215. })
  216. })
  217. describe('通道列表', () => {
  218. it('应该能够显示通道弹窗', async () => {
  219. const wrapper = mountCamera()
  220. await flushPromises()
  221. expect(wrapper.html()).toBeDefined()
  222. })
  223. })
  224. describe('状态过滤', () => {
  225. it('选择在线状态应该过滤列表', async () => {
  226. const wrapper = mountCamera()
  227. await flushPromises()
  228. expect(wrapper.html()).toBeDefined()
  229. })
  230. it('选择离线状态应该过滤列表', async () => {
  231. const wrapper = mountCamera()
  232. await flushPromises()
  233. expect(wrapper.html()).toBeDefined()
  234. })
  235. })
  236. describe('错误处理', () => {
  237. it('API 返回错误码应该正确处理', async () => {
  238. mockAdminListCameras.mockResolvedValue(wrapResponse([], 500, '获取失败'))
  239. const wrapper = mountCamera()
  240. await flushPromises()
  241. expect(mockAdminListCameras).toHaveBeenCalled()
  242. })
  243. })
  244. })