index.spec.ts 10 KB

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