index.spec.ts 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345
  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 DashboardView from '@/views/dashboard/index.vue'
  6. import { wrapResponse, mockDashboardStats } 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. }
  20. }))
  21. // Mock __APP_VERSION__
  22. vi.stubGlobal('__APP_VERSION__', '1.0.0')
  23. // Mock stats API
  24. const mockGetDashboardStats = vi.fn()
  25. vi.mock('@/api/stats', () => ({
  26. getDashboardStats: () => mockGetDashboardStats()
  27. }))
  28. // Create i18n instance
  29. const i18n = createI18n({
  30. legacy: false,
  31. locale: 'zh-CN',
  32. messages: {
  33. 'zh-CN': {
  34. 仪表盘: '仪表盘',
  35. '欢迎回来,这是您的数据概览': '欢迎回来,这是您的数据概览',
  36. 机器总数: '机器总数',
  37. 已启用: '已启用',
  38. 已禁用: '已禁用',
  39. 摄像头总数: '摄像头总数',
  40. 在线: '在线',
  41. 离线: '离线',
  42. 通道总数: '通道总数',
  43. 可用通道数量: '可用通道数量',
  44. 摄像头在线率: '摄像头在线率',
  45. 系统运行正常: '系统运行正常',
  46. 快捷操作: '快捷操作',
  47. 刷新数据: '刷新数据',
  48. 摄像头管理: '摄像头管理',
  49. 机器管理: '机器管理',
  50. 'Stream 测试': 'Stream 测试',
  51. 观看统计: '观看统计',
  52. 系统信息: '系统信息',
  53. 系统状态: '系统状态',
  54. 正常: '正常',
  55. 数据更新时间: '数据更新时间',
  56. 版本: '版本',
  57. 获取统计数据失败: '获取统计数据失败'
  58. }
  59. }
  60. })
  61. describe('Dashboard View', () => {
  62. beforeEach(() => {
  63. setActivePinia(createPinia())
  64. vi.clearAllMocks()
  65. mockGetDashboardStats.mockResolvedValue(wrapResponse(mockDashboardStats))
  66. mockPush.mockClear()
  67. })
  68. const mountDashboard = () => {
  69. return mount(DashboardView, {
  70. global: {
  71. plugins: [createPinia(), i18n]
  72. }
  73. })
  74. }
  75. describe('页面渲染', () => {
  76. it('应该正确渲染仪表盘页面', async () => {
  77. const wrapper = mountDashboard()
  78. await flushPromises()
  79. expect(wrapper.find('.dashboard').exists()).toBe(true)
  80. expect(wrapper.find('.dashboard__header').exists()).toBe(true)
  81. expect(wrapper.find('.dashboard__title').exists()).toBe(true)
  82. })
  83. it('应该显示页面标题', async () => {
  84. const wrapper = mountDashboard()
  85. await flushPromises()
  86. expect(wrapper.find('.dashboard__title').text()).toBe('仪表盘')
  87. })
  88. it('应该显示统计卡片', async () => {
  89. const wrapper = mountDashboard()
  90. await flushPromises()
  91. expect(wrapper.find('.dashboard__stats').exists()).toBe(true)
  92. expect(wrapper.findAll('.dashboard__card').length).toBe(4)
  93. })
  94. it('应该显示快捷操作按钮', async () => {
  95. const wrapper = mountDashboard()
  96. await flushPromises()
  97. expect(wrapper.find('.dashboard__actions').exists()).toBe(true)
  98. expect(wrapper.findAll('.dashboard__action').length).toBeGreaterThan(0)
  99. })
  100. it('应该显示系统信息', async () => {
  101. const wrapper = mountDashboard()
  102. await flushPromises()
  103. expect(wrapper.find('.dashboard__info').exists()).toBe(true)
  104. })
  105. })
  106. describe('数据加载', () => {
  107. it('页面加载时应该调用获取统计数据 API', async () => {
  108. mountDashboard()
  109. await flushPromises()
  110. expect(mockGetDashboardStats).toHaveBeenCalled()
  111. })
  112. it('应该正确显示机器总数', async () => {
  113. const wrapper = mountDashboard()
  114. await flushPromises()
  115. const cards = wrapper.findAll('.dashboard__card')
  116. const machineCard = cards[0]
  117. expect(machineCard.find('.dashboard__card-value').text()).toBe(String(mockDashboardStats.machineTotal))
  118. })
  119. it('应该正确显示摄像头总数', async () => {
  120. const wrapper = mountDashboard()
  121. await flushPromises()
  122. const cards = wrapper.findAll('.dashboard__card')
  123. const cameraCard = cards[1]
  124. expect(cameraCard.find('.dashboard__card-value').text()).toBe(String(mockDashboardStats.cameraTotal))
  125. })
  126. it('应该正确显示通道总数', async () => {
  127. const wrapper = mountDashboard()
  128. await flushPromises()
  129. const cards = wrapper.findAll('.dashboard__card')
  130. const channelCard = cards[2]
  131. expect(channelCard.find('.dashboard__card-value').text()).toBe(String(mockDashboardStats.channelTotal))
  132. })
  133. it('应该正确计算并显示在线率', async () => {
  134. const wrapper = mountDashboard()
  135. await flushPromises()
  136. const expectedRate = Math.round((mockDashboardStats.cameraOnline / mockDashboardStats.cameraTotal) * 100)
  137. const cards = wrapper.findAll('.dashboard__card')
  138. const rateCard = cards[3]
  139. expect(rateCard.find('.dashboard__card-value').text()).toContain(String(expectedRate))
  140. })
  141. })
  142. describe('快捷操作导航', () => {
  143. it('点击摄像头管理应该跳转', async () => {
  144. const wrapper = mountDashboard()
  145. await flushPromises()
  146. const actions = wrapper.findAll('.dashboard__action')
  147. const cameraAction = actions.find((a) => a.text().includes('摄像头管理'))
  148. if (cameraAction) {
  149. await cameraAction.trigger('click')
  150. expect(mockPush).toHaveBeenCalledWith('/camera')
  151. }
  152. })
  153. it('点击机器管理应该跳转', async () => {
  154. const wrapper = mountDashboard()
  155. await flushPromises()
  156. const actions = wrapper.findAll('.dashboard__action')
  157. const machineAction = actions.find((a) => a.text().includes('机器管理'))
  158. if (machineAction) {
  159. await machineAction.trigger('click')
  160. expect(mockPush).toHaveBeenCalledWith('/machine')
  161. }
  162. })
  163. it('点击 Stream 测试应该跳转', async () => {
  164. const wrapper = mountDashboard()
  165. await flushPromises()
  166. const actions = wrapper.findAll('.dashboard__action')
  167. const streamAction = actions.find((a) => a.text().includes('Stream 测试'))
  168. if (streamAction) {
  169. await streamAction.trigger('click')
  170. expect(mockPush).toHaveBeenCalledWith('/stream-test')
  171. }
  172. })
  173. it('点击观看统计应该跳转', async () => {
  174. const wrapper = mountDashboard()
  175. await flushPromises()
  176. const actions = wrapper.findAll('.dashboard__action')
  177. const statsAction = actions.find((a) => a.text().includes('观看统计'))
  178. if (statsAction) {
  179. await statsAction.trigger('click')
  180. expect(mockPush).toHaveBeenCalledWith('/stats')
  181. }
  182. })
  183. })
  184. describe('刷新数据', () => {
  185. it('点击刷新按钮应该重新加载数据', async () => {
  186. const wrapper = mountDashboard()
  187. await flushPromises()
  188. mockGetDashboardStats.mockClear()
  189. const refreshBtn = wrapper.find('.dashboard__refresh')
  190. await refreshBtn.trigger('click')
  191. await flushPromises()
  192. expect(mockGetDashboardStats).toHaveBeenCalled()
  193. })
  194. it('刷新时应该显示加载状态', async () => {
  195. let resolvePromise: Function
  196. mockGetDashboardStats.mockImplementation(
  197. () =>
  198. new Promise((resolve) => {
  199. resolvePromise = resolve
  200. })
  201. )
  202. const wrapper = mountDashboard()
  203. await flushPromises()
  204. mockGetDashboardStats.mockClear()
  205. mockGetDashboardStats.mockImplementation(
  206. () =>
  207. new Promise((resolve) => {
  208. resolvePromise = () => resolve(wrapResponse(mockDashboardStats))
  209. })
  210. )
  211. const refreshBtn = wrapper.find('.dashboard__refresh')
  212. await refreshBtn.trigger('click')
  213. // 检查加载状态图标
  214. expect(wrapper.find('.dashboard__refresh-icon').exists()).toBe(true)
  215. resolvePromise!()
  216. await flushPromises()
  217. })
  218. })
  219. describe('错误处理', () => {
  220. it('获取数据失败应该显示错误消息', async () => {
  221. const { ElMessage } = await import('element-plus')
  222. mockGetDashboardStats.mockResolvedValue(wrapResponse(null, 500, '服务器错误'))
  223. mountDashboard()
  224. await flushPromises()
  225. // 错误处理逻辑测试
  226. expect(mockGetDashboardStats).toHaveBeenCalled()
  227. })
  228. it('网络错误应该显示错误消息', async () => {
  229. mockGetDashboardStats.mockResolvedValue(wrapResponse(null, 500, '网络错误'))
  230. mountDashboard()
  231. await flushPromises()
  232. expect(mockGetDashboardStats).toHaveBeenCalled()
  233. })
  234. })
  235. describe('系统信息', () => {
  236. it('应该显示系统状态为正常', async () => {
  237. const wrapper = mountDashboard()
  238. await flushPromises()
  239. expect(wrapper.find('.dashboard__badge--success').text()).toBe('正常')
  240. })
  241. it('应该显示版本号', async () => {
  242. const wrapper = mountDashboard()
  243. await flushPromises()
  244. expect(wrapper.text()).toContain('v1.0.0')
  245. })
  246. it('数据更新后应该显示更新时间', async () => {
  247. const wrapper = mountDashboard()
  248. await flushPromises()
  249. const infoItems = wrapper.findAll('.dashboard__info-item')
  250. const updateTimeItem = infoItems.find((item) => item.text().includes('数据更新时间'))
  251. expect(updateTimeItem).toBeDefined()
  252. })
  253. })
  254. describe('空数据处理', () => {
  255. it('无数据时应该显示默认值', async () => {
  256. mockGetDashboardStats.mockResolvedValue(wrapResponse(null))
  257. const wrapper = mountDashboard()
  258. await flushPromises()
  259. const cards = wrapper.findAll('.dashboard__card')
  260. // 默认值为 0
  261. expect(cards[0].find('.dashboard__card-value').text()).toBe('0')
  262. })
  263. it('在线率为零时应该显示 0%', async () => {
  264. mockGetDashboardStats.mockResolvedValue(
  265. wrapResponse({
  266. ...mockDashboardStats,
  267. cameraTotal: 0,
  268. cameraOnline: 0
  269. })
  270. )
  271. const wrapper = mountDashboard()
  272. await flushPromises()
  273. const cards = wrapper.findAll('.dashboard__card')
  274. const rateCard = cards[3]
  275. expect(rateCard.find('.dashboard__card-value').text()).toContain('0')
  276. })
  277. })
  278. })