feature: Simulation Control Center (Dashboard)
CI / backend (push) Successful in 8m43s
CI / frontend (push) Successful in 2m0s

This commit is contained in:
voltsrage
2026-08-06 02:22:55 +08:00
parent 24f45851e9
commit 1d28880920
91 changed files with 1943 additions and 25 deletions
@@ -0,0 +1,100 @@
import { describe, it, expect, beforeEach, vi } from 'vitest'
import { mount } from '@vue/test-utils'
import { createPinia, setActivePinia } from 'pinia'
import SimulationControlView from '@/views/SimulationControlView.vue'
const {
fetchScenarios,
fetchRuns,
fetchSimulationConfig,
startRun,
stopRun,
} = vi.hoisted(() => ({
fetchScenarios: vi.fn(),
fetchRuns: vi.fn(),
fetchSimulationConfig: vi.fn(),
startRun: vi.fn(),
stopRun: vi.fn(),
}))
vi.mock('@/api/simulation', () => ({
fetchScenarios,
fetchRuns,
fetchSimulationConfig,
startRun,
stopRun,
fetchRun: vi.fn(),
}))
vi.mock('@/stores/ward', () => ({
useWardStore: () => ({ loadEncounters: vi.fn() }),
}))
const scenarios = [
{
id: 'uti-sepsis-elderly-01',
name: 'UTI Sepsis Elderly',
description: 'Elderly patient with UTI progressing to sepsis.',
durationMinutes: 480,
tags: ['sepsis', 'uti'],
department: 'GeneralMedicine',
eventCount: 200,
},
{
id: 'stable-baseline-01',
name: 'Stable Baseline',
description: 'Control scenario.',
durationMinutes: 480,
tags: ['control', 'stable'],
department: 'Surgery',
eventCount: 312,
},
]
describe('SimulationControlView', () => {
beforeEach(() => {
vi.clearAllMocks()
setActivePinia(createPinia())
fetchScenarios.mockResolvedValue(scenarios)
fetchRuns.mockResolvedValue([])
fetchSimulationConfig.mockResolvedValue({
enabled: true,
maxSpeed: 600,
maxConcurrentRuns: 2,
})
})
it('showsWallClockDurationForSelectedScenarioSpeed', async () => {
const wrapper = mount(SimulationControlView)
await vi.waitFor(() => expect(wrapper.text()).toContain('UTI Sepsis Elderly'))
// Default Fast (60×): 480 sim minutes → 8 wall-clock minutes
expect(wrapper.text()).toMatch(/8 minutes for selected scenario/)
const realTime = wrapper.findAll('button').find(b => b.text().includes('Real time'))
expect(realTime.text()).toMatch(/8 hours for selected scenario/)
const instant = wrapper.findAll('button').find(b => b.text().includes('Instant'))
expect(instant.text()).toMatch(/1 minute for selected scenario/)
})
it('filtersCatalogueByTag', async () => {
const wrapper = mount(SimulationControlView)
await vi.waitFor(() => expect(wrapper.text()).toContain('Stable Baseline'))
const sepsisChip = wrapper.findAll('button').find(b => b.text() === 'sepsis')
await sepsisChip.trigger('click')
expect(wrapper.text()).toContain('UTI Sepsis Elderly')
expect(wrapper.text()).not.toContain('Stable Baseline')
})
it('showsEmptyStateWhenCatalogueMissing', async () => {
fetchScenarios.mockResolvedValue([])
const wrapper = mount(SimulationControlView)
await vi.waitFor(() =>
expect(wrapper.text()).toContain('Simulation:ScenarioDirectory'),
)
})
})