import test from 'node:test' import assert from 'node:assert/strict' import { readFile } from 'node:fs/promises' import vm from 'node:vm' import { computed, effectScope, nextTick, ref, watch } from 'vue' import { parse } from '@vue/compiler-sfc' const filename = new URL('../src/views/subSystem/alarm/common/remind.vue', import.meta.url) const { descriptor } = parse(await readFile(filename, 'utf8')) // 执行实际页面脚本,用可控请求和生命周期验证异步显示,不依赖真实接口或等待轮询。 const script = descriptor.scriptSetup.content.replace(/^import .+$/gm, '') function mountReminder(t) { const requests = { reminders: [], statistics: [], options: [] } const lifecycle = {}, timers = new Map(), scope = ref({ rootTypeId: 2 }) const request = name => params => new Promise((resolve, reject) => { requests[name].push({ params, resolve, reject }) }) const context = { computed, ref, watch, useAlarmScope: () => ({ scope, scopeKey: computed(() => JSON.stringify(scope.value)) }), getAlarmReminders: request('reminders'), getAlarmStatistics: request('statistics'), getAlarmOptions: request('options'), onMounted: callback => { lifecycle.mounted = callback }, onActivated: callback => { lifecycle.activated = callback }, onDeactivated: callback => { lifecycle.deactivated = callback }, onBeforeUnmount: callback => { lifecycle.unmount = callback }, setInterval: (callback, delay) => { timers.set(delay, callback); return delay }, clearInterval: timer => timers.delete(timer) } const effects = effectScope() effects.run(() => vm.runInNewContext(`${script} globalThis.page = { visibleRows, received, loading, hasLoaded, stats, options, error, refreshError, manualPause, hoverPause, focusPause, detailPause, pending, level, load, restart, retry } `, context)) t.after(() => { lifecycle.unmount(); effects.stop() }) return { page: context.page, requests, lifecycle, timers, scope } } async function flush() { for (let i = 0; i < 5; i += 1) await Promise.resolve() await nextTick() } const rowsResponse = records => ({ data: { records } }) const event = id => ({ id, latestOperationTime: '2026-09-24 10:00:00' }) test('first response displays immediately without waiting for statistics, options or polling', async t => { const { page, requests, lifecycle, timers } = mountReminder(t) assert.equal(requests.reminders.length, 1) assert.equal(timers.size, 0) lifecycle.mounted() lifecycle.activated() assert.equal(requests.reminders.length, 1) assert.ok(timers.has(30000)) const rows = [event(1)] requests.reminders[0].resolve(rowsResponse(rows)) await flush() assert.deepEqual(page.visibleRows.value, rows) assert.equal(page.loading.value, false) assert.equal(page.hasLoaded.value, true) requests.statistics[0].resolve({ data: [{ level: 1, total: 3 }] }) await flush() assert.equal(page.stats.value[0].total, 3) assert.deepEqual(page.visibleRows.value, rows) }) for (const pause of ['hoverPause', 'focusPause', 'manualPause', 'detailPause']) { test(`first response still displays while ${pause} is active`, async t => { const { page, requests } = mountReminder(t) page[pause].value = true requests.reminders[0].resolve(rowsResponse([event(1)])) await flush() assert.equal(page.visibleRows.value[0]?.id, 1) assert.equal(page.pending.value, 0) assert.equal(page.loading.value, false) }) } test('subsequent polling preserves paused reading and releases updates when unpaused', async t => { const { page, requests, lifecycle, timers } = mountReminder(t) lifecycle.mounted() requests.reminders[0].resolve(rowsResponse([event(1)])) await flush() page.hoverPause.value = true timers.get(30000)() assert.equal(page.loading.value, true) assert.equal(page.hasLoaded.value, true) assert.equal(page.visibleRows.value[0].id, 1) requests.reminders[1].resolve(rowsResponse([event(2)])) await flush() assert.equal(page.visibleRows.value[0].id, 1) assert.equal(page.received.value[0].id, 2) assert.equal(page.pending.value, 1) page.hoverPause.value = false await flush() assert.equal(page.visibleRows.value[0].id, 2) assert.equal(page.pending.value, 0) }) test('an initially empty list shows newly received events even while paused', async t => { const { page, requests } = mountReminder(t) requests.reminders[0].resolve(rowsResponse([])) await flush() page.hoverPause.value = true page.load() requests.reminders[1].resolve(rowsResponse([event(1)])) await flush() assert.equal(page.visibleRows.value[0]?.id, 1) }) test('statistics and options failures do not hide reminders and can be retried', async t => { const { page, requests } = mountReminder(t) requests.statistics[0].reject(new Error('statistics failed')) requests.options[0].reject(new Error('options failed')) requests.reminders[0].resolve(rowsResponse([event(1)])) await flush() assert.equal(page.loading.value, false) assert.equal(page.visibleRows.value.length, 1) assert.equal(page.refreshError.value, 'statistics failed') page.retry() assert.equal(requests.options.length, 2) requests.statistics[1].resolve({ data: [] }) requests.options[1].resolve({ data: { alarmLevels: [] } }) requests.reminders[1].resolve(rowsResponse([event(2)])) await flush() assert.equal(page.refreshError.value, '') assert.equal(page.visibleRows.value[0].id, 2) }) test('a failed list request stops loading immediately and recovers while paused', async t => { const { page, requests } = mountReminder(t) requests.reminders[0].reject(new Error('reminders failed')) await flush() assert.equal(page.loading.value, false) assert.equal(page.error.value, 'reminders failed') requests.statistics[0].resolve({ data: [] }) await flush() assert.equal(page.error.value, 'reminders failed') page.focusPause.value = true page.retry() requests.reminders[1].resolve(rowsResponse([event(1)])) await flush() assert.equal(page.error.value, '') assert.equal(page.visibleRows.value[0].id, 1) }) test('changing scope rejects older responses and displays the new scope while paused', async t => { const { page, requests, scope } = mountReminder(t) page.hoverPause.value = true scope.value = { rootTypeId: 1 } await flush() assert.equal(requests.reminders[1].params.rootTypeId, 1) requests.reminders[1].resolve(rowsResponse([event(2)])) requests.statistics[1].resolve({ data: [{ level: 1, total: 2 }] }) requests.options[1].resolve({ data: { name: 'current' } }) await flush() requests.reminders[0].resolve(rowsResponse([event(1)])) requests.statistics[0].resolve({ data: [{ level: 1, total: 99 }] }) requests.options[0].reject(new Error('old scope failed')) await flush() assert.equal(page.visibleRows.value[0].id, 2) assert.equal(page.stats.value[0].total, 2) assert.equal(page.options.value.name, 'current') assert.equal(page.refreshError.value, '') }) test('filter changes show the new result immediately despite manual pause', async t => { const { page, requests } = mountReminder(t) requests.reminders[0].resolve(rowsResponse([event(1)])) await flush() page.manualPause.value = true page.level.value = 2 page.restart() assert.equal(requests.reminders[1].params.alarmLevel, 2) requests.reminders[1].resolve(rowsResponse([event(2)])) await flush() assert.equal(page.visibleRows.value[0].id, 2) assert.equal(page.manualPause.value, true) }) test('returning to a cached page refreshes immediately and ignores pre-deactivation requests', async t => { const { page, requests, lifecycle } = mountReminder(t) requests.reminders[0].resolve(rowsResponse([event(1)])) await flush() page.hoverPause.value = true page.focusPause.value = true page.manualPause.value = true page.load() lifecycle.deactivated() page.load() assert.equal(requests.reminders.length, 2) assert.equal(page.hoverPause.value, false) assert.equal(page.focusPause.value, false) lifecycle.activated() assert.equal(requests.reminders.length, 3) requests.reminders[1].resolve(rowsResponse([event(2)])) await flush() assert.equal(page.loading.value, true) assert.equal(page.visibleRows.value[0].id, 1) requests.reminders[2].resolve(rowsResponse([event(3)])) await flush() assert.equal(page.loading.value, false) assert.equal(page.visibleRows.value[0].id, 3) assert.equal(page.manualPause.value, true) })