feature: Self-Service Clinical Testing Sessions
CI / backend (push) Successful in 8m52s
CI / frontend (push) Failing after 1m39s

This commit is contained in:
voltsrage
2026-08-06 04:03:04 +08:00
parent 1d28880920
commit 80b009fd23
41 changed files with 4922 additions and 123 deletions
@@ -5,24 +5,36 @@ import SimulationControlView from '@/views/SimulationControlView.vue'
const {
fetchScenarios,
fetchSessions,
fetchRuns,
fetchSimulationConfig,
fetchDataSummary,
startRun,
startSession,
stopRun,
purgeData,
} = vi.hoisted(() => ({
fetchScenarios: vi.fn(),
fetchSessions: vi.fn(),
fetchRuns: vi.fn(),
fetchSimulationConfig: vi.fn(),
fetchDataSummary: vi.fn(),
startRun: vi.fn(),
startSession: vi.fn(),
stopRun: vi.fn(),
purgeData: vi.fn(),
}))
vi.mock('@/api/simulation', () => ({
fetchScenarios,
fetchSessions,
fetchRuns,
fetchSimulationConfig,
fetchDataSummary,
startRun,
startSession,
stopRun,
purgeData,
fetchRun: vi.fn(),
}))
@@ -30,6 +42,10 @@ vi.mock('@/stores/ward', () => ({
useWardStore: () => ({ loadEncounters: vi.fn() }),
}))
vi.mock('@/stores/alerts', () => ({
useAlertStore: () => ({ loadGlobalAlerts: vi.fn() }),
}))
const scenarios = [
{
id: 'uti-sepsis-elderly-01',
@@ -51,12 +67,34 @@ const scenarios = [
},
]
const sessions = [
{
id: 'session-a-orientation',
name: 'Session A — Quick orientation',
goal: 'Learn the interface.',
estimatedMinutes: 25,
defaultSpeed: 60,
scenarios: [
{ id: 'stable-baseline-01', name: 'Stable Baseline' },
{ id: 'uti-sepsis-elderly-01', name: 'UTI Sepsis Elderly' },
],
},
]
describe('SimulationControlView', () => {
beforeEach(() => {
vi.clearAllMocks()
setActivePinia(createPinia())
fetchScenarios.mockResolvedValue(scenarios)
fetchSessions.mockResolvedValue(sessions)
fetchRuns.mockResolvedValue([])
fetchDataSummary.mockResolvedValue({
simulatedPatients: 0,
encounters: 0,
observations: 0,
alerts: 0,
activeRuns: 0,
})
fetchSimulationConfig.mockResolvedValue({
enabled: true,
maxSpeed: 600,
@@ -64,8 +102,30 @@ describe('SimulationControlView', () => {
})
})
it('defaultsToSessionsTab', async () => {
const wrapper = mount(SimulationControlView)
await vi.waitFor(() => expect(wrapper.text()).toContain('Session A — Quick orientation'))
expect(wrapper.text()).toContain('Testing sessions')
expect(wrapper.text()).not.toContain('Scenario catalogue')
})
it('switchesToScenariosTab', async () => {
const wrapper = mount(SimulationControlView)
await vi.waitFor(() => expect(wrapper.text()).toContain('Session A'))
const scenariosTab = wrapper.findAll('button').find(b => b.text() === 'Scenarios')
await scenariosTab.trigger('click')
await vi.waitFor(() => expect(wrapper.text()).toContain('Scenario catalogue'))
expect(wrapper.text()).toContain('UTI Sepsis Elderly')
})
it('showsWallClockDurationForSelectedScenarioSpeed', async () => {
const wrapper = mount(SimulationControlView)
await vi.waitFor(() => expect(wrapper.text()).toContain('Session A'))
const scenariosTab = wrapper.findAll('button').find(b => b.text() === 'Scenarios')
await scenariosTab.trigger('click')
await vi.waitFor(() => expect(wrapper.text()).toContain('UTI Sepsis Elderly'))
// Default Fast (60×): 480 sim minutes → 8 wall-clock minutes
@@ -78,9 +138,12 @@ describe('SimulationControlView', () => {
expect(instant.text()).toMatch(/1 minute for selected scenario/)
})
it('filtersCatalogueByTag', async () => {
const wrapper = mount(SimulationControlView)
await vi.waitFor(() => expect(wrapper.text()).toContain('Session A'))
const scenariosTab = wrapper.findAll('button').find(b => b.text() === 'Scenarios')
await scenariosTab.trigger('click')
await vi.waitFor(() => expect(wrapper.text()).toContain('Stable Baseline'))
const sepsisChip = wrapper.findAll('button').find(b => b.text() === 'sepsis')
@@ -93,6 +156,11 @@ describe('SimulationControlView', () => {
it('showsEmptyStateWhenCatalogueMissing', async () => {
fetchScenarios.mockResolvedValue([])
const wrapper = mount(SimulationControlView)
await vi.waitFor(() => expect(wrapper.text()).toContain('Session A'))
const scenariosTab = wrapper.findAll('button').find(b => b.text() === 'Scenarios')
await scenariosTab.trigger('click')
await vi.waitFor(() =>
expect(wrapper.text()).toContain('Simulation:ScenarioDirectory'),
)