alarmRemindLoading.test.mjs 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. import test from 'node:test'
  2. import assert from 'node:assert/strict'
  3. import { readFile } from 'node:fs/promises'
  4. import vm from 'node:vm'
  5. import { computed, effectScope, nextTick, ref, watch } from 'vue'
  6. import { parse } from '@vue/compiler-sfc'
  7. const filename = new URL('../src/views/subSystem/alarm/common/remind.vue', import.meta.url)
  8. const { descriptor } = parse(await readFile(filename, 'utf8'))
  9. // 执行实际页面脚本,用可控请求和生命周期验证异步显示,不依赖真实接口或等待轮询。
  10. const script = descriptor.scriptSetup.content.replace(/^import .+$/gm, '')
  11. function mountReminder(t) {
  12. const requests = { reminders: [], statistics: [], options: [] }
  13. const lifecycle = {}, timers = new Map(), scope = ref({ rootTypeId: 2 })
  14. const request = name => params => new Promise((resolve, reject) => {
  15. requests[name].push({ params, resolve, reject })
  16. })
  17. const context = {
  18. computed, ref, watch,
  19. useAlarmScope: () => ({ scope, scopeKey: computed(() => JSON.stringify(scope.value)) }),
  20. getAlarmReminders: request('reminders'),
  21. getAlarmStatistics: request('statistics'),
  22. getAlarmOptions: request('options'),
  23. onMounted: callback => { lifecycle.mounted = callback },
  24. onActivated: callback => { lifecycle.activated = callback },
  25. onDeactivated: callback => { lifecycle.deactivated = callback },
  26. onBeforeUnmount: callback => { lifecycle.unmount = callback },
  27. setInterval: (callback, delay) => { timers.set(delay, callback); return delay },
  28. clearInterval: timer => timers.delete(timer)
  29. }
  30. const effects = effectScope()
  31. effects.run(() => vm.runInNewContext(`${script}
  32. globalThis.page = {
  33. visibleRows, received, loading, hasLoaded, stats, options, error, refreshError,
  34. manualPause, hoverPause, focusPause, detailPause, pending, level,
  35. load, restart, retry
  36. }
  37. `, context))
  38. t.after(() => { lifecycle.unmount(); effects.stop() })
  39. return { page: context.page, requests, lifecycle, timers, scope }
  40. }
  41. async function flush() {
  42. for (let i = 0; i < 5; i += 1) await Promise.resolve()
  43. await nextTick()
  44. }
  45. const rowsResponse = records => ({ data: { records } })
  46. const event = id => ({ id, latestOperationTime: '2026-09-24 10:00:00' })
  47. test('first response displays immediately without waiting for statistics, options or polling', async t => {
  48. const { page, requests, lifecycle, timers } = mountReminder(t)
  49. assert.equal(requests.reminders.length, 1)
  50. assert.equal(timers.size, 0)
  51. lifecycle.mounted()
  52. lifecycle.activated()
  53. assert.equal(requests.reminders.length, 1)
  54. assert.ok(timers.has(30000))
  55. const rows = [event(1)]
  56. requests.reminders[0].resolve(rowsResponse(rows))
  57. await flush()
  58. assert.deepEqual(page.visibleRows.value, rows)
  59. assert.equal(page.loading.value, false)
  60. assert.equal(page.hasLoaded.value, true)
  61. requests.statistics[0].resolve({ data: [{ level: 1, total: 3 }] })
  62. await flush()
  63. assert.equal(page.stats.value[0].total, 3)
  64. assert.deepEqual(page.visibleRows.value, rows)
  65. })
  66. for (const pause of ['hoverPause', 'focusPause', 'manualPause', 'detailPause']) {
  67. test(`first response still displays while ${pause} is active`, async t => {
  68. const { page, requests } = mountReminder(t)
  69. page[pause].value = true
  70. requests.reminders[0].resolve(rowsResponse([event(1)]))
  71. await flush()
  72. assert.equal(page.visibleRows.value[0]?.id, 1)
  73. assert.equal(page.pending.value, 0)
  74. assert.equal(page.loading.value, false)
  75. })
  76. }
  77. test('subsequent polling preserves paused reading and releases updates when unpaused', async t => {
  78. const { page, requests, lifecycle, timers } = mountReminder(t)
  79. lifecycle.mounted()
  80. requests.reminders[0].resolve(rowsResponse([event(1)]))
  81. await flush()
  82. page.hoverPause.value = true
  83. timers.get(30000)()
  84. assert.equal(page.loading.value, true)
  85. assert.equal(page.hasLoaded.value, true)
  86. assert.equal(page.visibleRows.value[0].id, 1)
  87. requests.reminders[1].resolve(rowsResponse([event(2)]))
  88. await flush()
  89. assert.equal(page.visibleRows.value[0].id, 1)
  90. assert.equal(page.received.value[0].id, 2)
  91. assert.equal(page.pending.value, 1)
  92. page.hoverPause.value = false
  93. await flush()
  94. assert.equal(page.visibleRows.value[0].id, 2)
  95. assert.equal(page.pending.value, 0)
  96. })
  97. test('an initially empty list shows newly received events even while paused', async t => {
  98. const { page, requests } = mountReminder(t)
  99. requests.reminders[0].resolve(rowsResponse([]))
  100. await flush()
  101. page.hoverPause.value = true
  102. page.load()
  103. requests.reminders[1].resolve(rowsResponse([event(1)]))
  104. await flush()
  105. assert.equal(page.visibleRows.value[0]?.id, 1)
  106. })
  107. test('statistics and options failures do not hide reminders and can be retried', async t => {
  108. const { page, requests } = mountReminder(t)
  109. requests.statistics[0].reject(new Error('statistics failed'))
  110. requests.options[0].reject(new Error('options failed'))
  111. requests.reminders[0].resolve(rowsResponse([event(1)]))
  112. await flush()
  113. assert.equal(page.loading.value, false)
  114. assert.equal(page.visibleRows.value.length, 1)
  115. assert.equal(page.refreshError.value, 'statistics failed')
  116. page.retry()
  117. assert.equal(requests.options.length, 2)
  118. requests.statistics[1].resolve({ data: [] })
  119. requests.options[1].resolve({ data: { alarmLevels: [] } })
  120. requests.reminders[1].resolve(rowsResponse([event(2)]))
  121. await flush()
  122. assert.equal(page.refreshError.value, '')
  123. assert.equal(page.visibleRows.value[0].id, 2)
  124. })
  125. test('a failed list request stops loading immediately and recovers while paused', async t => {
  126. const { page, requests } = mountReminder(t)
  127. requests.reminders[0].reject(new Error('reminders failed'))
  128. await flush()
  129. assert.equal(page.loading.value, false)
  130. assert.equal(page.error.value, 'reminders failed')
  131. requests.statistics[0].resolve({ data: [] })
  132. await flush()
  133. assert.equal(page.error.value, 'reminders failed')
  134. page.focusPause.value = true
  135. page.retry()
  136. requests.reminders[1].resolve(rowsResponse([event(1)]))
  137. await flush()
  138. assert.equal(page.error.value, '')
  139. assert.equal(page.visibleRows.value[0].id, 1)
  140. })
  141. test('changing scope rejects older responses and displays the new scope while paused', async t => {
  142. const { page, requests, scope } = mountReminder(t)
  143. page.hoverPause.value = true
  144. scope.value = { rootTypeId: 1 }
  145. await flush()
  146. assert.equal(requests.reminders[1].params.rootTypeId, 1)
  147. requests.reminders[1].resolve(rowsResponse([event(2)]))
  148. requests.statistics[1].resolve({ data: [{ level: 1, total: 2 }] })
  149. requests.options[1].resolve({ data: { name: 'current' } })
  150. await flush()
  151. requests.reminders[0].resolve(rowsResponse([event(1)]))
  152. requests.statistics[0].resolve({ data: [{ level: 1, total: 99 }] })
  153. requests.options[0].reject(new Error('old scope failed'))
  154. await flush()
  155. assert.equal(page.visibleRows.value[0].id, 2)
  156. assert.equal(page.stats.value[0].total, 2)
  157. assert.equal(page.options.value.name, 'current')
  158. assert.equal(page.refreshError.value, '')
  159. })
  160. test('filter changes show the new result immediately despite manual pause', async t => {
  161. const { page, requests } = mountReminder(t)
  162. requests.reminders[0].resolve(rowsResponse([event(1)]))
  163. await flush()
  164. page.manualPause.value = true
  165. page.level.value = 2
  166. page.restart()
  167. assert.equal(requests.reminders[1].params.alarmLevel, 2)
  168. requests.reminders[1].resolve(rowsResponse([event(2)]))
  169. await flush()
  170. assert.equal(page.visibleRows.value[0].id, 2)
  171. assert.equal(page.manualPause.value, true)
  172. })
  173. test('returning to a cached page refreshes immediately and ignores pre-deactivation requests', async t => {
  174. const { page, requests, lifecycle } = mountReminder(t)
  175. requests.reminders[0].resolve(rowsResponse([event(1)]))
  176. await flush()
  177. page.hoverPause.value = true
  178. page.focusPause.value = true
  179. page.manualPause.value = true
  180. page.load()
  181. lifecycle.deactivated()
  182. page.load()
  183. assert.equal(requests.reminders.length, 2)
  184. assert.equal(page.hoverPause.value, false)
  185. assert.equal(page.focusPause.value, false)
  186. lifecycle.activated()
  187. assert.equal(requests.reminders.length, 3)
  188. requests.reminders[1].resolve(rowsResponse([event(2)]))
  189. await flush()
  190. assert.equal(page.loading.value, true)
  191. assert.equal(page.visibleRows.value[0].id, 1)
  192. requests.reminders[2].resolve(rowsResponse([event(3)]))
  193. await flush()
  194. assert.equal(page.loading.value, false)
  195. assert.equal(page.visibleRows.value[0].id, 3)
  196. assert.equal(page.manualPause.value, true)
  197. })