index.spec.ts 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  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 StatsView from '@/views/stats/index.vue'
  5. import { wrapResponse, mockDashboardStats } from '../../../fixtures'
  6. // Mock element-plus
  7. vi.mock('element-plus', () => ({
  8. ElMessage: {
  9. success: vi.fn(),
  10. error: vi.fn(),
  11. info: vi.fn()
  12. }
  13. }))
  14. // Mock stats API
  15. const mockGetDashboardStats = vi.fn()
  16. vi.mock('@/api/stats', () => ({
  17. getDashboardStats: () => mockGetDashboardStats()
  18. }))
  19. describe('Stats View', () => {
  20. beforeEach(() => {
  21. setActivePinia(createPinia())
  22. vi.clearAllMocks()
  23. mockGetDashboardStats.mockResolvedValue(wrapResponse(mockDashboardStats))
  24. })
  25. const mountStats = () => {
  26. return mount(StatsView, {
  27. global: {
  28. plugins: [createPinia()],
  29. stubs: {
  30. 'el-card': { template: '<div class="el-card"><slot /><slot name="header" /></div>', props: ['shadow'] },
  31. 'el-form': { template: '<form class="el-form"><slot /></form>' },
  32. 'el-form-item': { template: '<div class="el-form-item"><slot /></div>', props: ['label'] },
  33. 'el-button': {
  34. template: '<button @click="$emit(\'click\')" :disabled="loading"><slot /></button>',
  35. props: ['type', 'icon', 'loading']
  36. },
  37. 'el-row': { template: '<div class="el-row"><slot /></div>', props: ['gutter'] },
  38. 'el-col': { template: '<div class="el-col"><slot /></div>', props: ['xs', 'sm', 'lg'] },
  39. 'el-statistic': {
  40. template:
  41. '<div class="el-statistic"><span class="title">{{ title }}</span><span class="value">{{ value }}</span><slot name="prefix" /></div>',
  42. props: ['title', 'value', 'suffix']
  43. },
  44. 'el-icon': { template: '<span class="el-icon"><slot /></span>' },
  45. 'el-empty': {
  46. template: '<div class="el-empty"><slot name="image" />{{ description }}</div>',
  47. props: ['description']
  48. },
  49. Monitor: { template: '<span>Monitor</span>' },
  50. VideoCamera: { template: '<span>VideoCamera</span>' },
  51. Connection: { template: '<span>Connection</span>' },
  52. CircleCheck: { template: '<span>CircleCheck</span>' },
  53. TrendCharts: { template: '<span>TrendCharts</span>' },
  54. Refresh: { template: '<span>Refresh</span>' }
  55. }
  56. }
  57. })
  58. }
  59. describe('页面渲染', () => {
  60. it('应该正确渲染统计页面', async () => {
  61. const wrapper = mountStats()
  62. await flushPromises()
  63. expect(wrapper.find('.page-container').exists()).toBe(true)
  64. })
  65. it('应该显示刷新按钮', async () => {
  66. const wrapper = mountStats()
  67. await flushPromises()
  68. const refreshBtn = wrapper.find('.filter-card button')
  69. expect(refreshBtn.exists()).toBe(true)
  70. expect(refreshBtn.text()).toContain('刷新')
  71. })
  72. it('应该显示统计卡片', async () => {
  73. const wrapper = mountStats()
  74. await flushPromises()
  75. expect(wrapper.find('.summary-cards').exists()).toBe(true)
  76. })
  77. it('应该显示状态图表区域', async () => {
  78. const wrapper = mountStats()
  79. await flushPromises()
  80. expect(wrapper.find('.status-card').exists()).toBe(true)
  81. })
  82. })
  83. describe('数据加载', () => {
  84. it('页面加载时应该获取统计数据', async () => {
  85. mountStats()
  86. await flushPromises()
  87. expect(mockGetDashboardStats).toHaveBeenCalled()
  88. })
  89. it('应该正确显示机器总数', async () => {
  90. const wrapper = mountStats()
  91. await flushPromises()
  92. const stats = wrapper.findAll('.el-statistic')
  93. expect(stats.length).toBeGreaterThan(0)
  94. })
  95. it('应该正确显示摄像头总数', async () => {
  96. const wrapper = mountStats()
  97. await flushPromises()
  98. expect(wrapper.html()).toContain(String(mockDashboardStats.cameraTotal))
  99. })
  100. it('应该正确显示通道总数', async () => {
  101. const wrapper = mountStats()
  102. await flushPromises()
  103. expect(wrapper.html()).toContain(String(mockDashboardStats.channelTotal))
  104. })
  105. it('应该正确计算在线率', async () => {
  106. const wrapper = mountStats()
  107. await flushPromises()
  108. const expectedRate = Math.round((mockDashboardStats.cameraOnline / mockDashboardStats.cameraTotal) * 100)
  109. expect(wrapper.html()).toContain(String(expectedRate))
  110. })
  111. })
  112. describe('刷新功能', () => {
  113. it('点击刷新按钮应该重新加载数据', async () => {
  114. const wrapper = mountStats()
  115. await flushPromises()
  116. mockGetDashboardStats.mockClear()
  117. const refreshBtn = wrapper.find('.filter-card button')
  118. await refreshBtn.trigger('click')
  119. await flushPromises()
  120. expect(mockGetDashboardStats).toHaveBeenCalled()
  121. })
  122. })
  123. describe('状态条显示', () => {
  124. it('应该显示在线状态条', async () => {
  125. const wrapper = mountStats()
  126. await flushPromises()
  127. expect(wrapper.find('.status-item.online').exists()).toBe(true)
  128. })
  129. it('应该显示离线状态条', async () => {
  130. const wrapper = mountStats()
  131. await flushPromises()
  132. expect(wrapper.find('.status-item.offline').exists()).toBe(true)
  133. })
  134. it('应该显示已启用状态条', async () => {
  135. const wrapper = mountStats()
  136. await flushPromises()
  137. expect(wrapper.find('.status-item.enabled').exists()).toBe(true)
  138. })
  139. it('应该显示已禁用状态条', async () => {
  140. const wrapper = mountStats()
  141. await flushPromises()
  142. expect(wrapper.find('.status-item.disabled').exists()).toBe(true)
  143. })
  144. })
  145. describe('更新时间显示', () => {
  146. it('数据加载后应该显示更新时间', async () => {
  147. const wrapper = mountStats()
  148. await flushPromises()
  149. expect(wrapper.find('.update-time').exists()).toBe(true)
  150. })
  151. })
  152. describe('错误处理', () => {
  153. it('API 返回错误码应该正确处理', async () => {
  154. mockGetDashboardStats.mockResolvedValue(wrapResponse(null, false, '服务器错误'))
  155. mountStats()
  156. await flushPromises()
  157. expect(mockGetDashboardStats).toHaveBeenCalled()
  158. })
  159. it('API 返回空数据应该正确处理', async () => {
  160. mockGetDashboardStats.mockResolvedValue(wrapResponse(null))
  161. mountStats()
  162. await flushPromises()
  163. expect(mockGetDashboardStats).toHaveBeenCalled()
  164. })
  165. })
  166. describe('空数据处理', () => {
  167. it('无数据时在线率应该为 0', async () => {
  168. mockGetDashboardStats.mockResolvedValue(
  169. wrapResponse({
  170. machineTotal: 0,
  171. machineEnabled: 0,
  172. cameraTotal: 0,
  173. cameraOnline: 0,
  174. cameraOffline: 0,
  175. channelTotal: 0
  176. })
  177. )
  178. const wrapper = mountStats()
  179. await flushPromises()
  180. expect(wrapper.html()).toContain('0')
  181. })
  182. })
  183. })