feature: Improve dashboard functionality
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
import { describe, it, expect, beforeEach } from 'vitest'
|
||||
import { mount } from '@vue/test-utils'
|
||||
import { createPinia, setActivePinia } from 'pinia'
|
||||
import AcknowledgeModal from '@/components/alerts/AcknowledgeModal.vue'
|
||||
import { useAuthStore } from '@/stores/auth'
|
||||
|
||||
const alert = {
|
||||
id: 'alert-1',
|
||||
alertType: 'SofaSepsis',
|
||||
severity: 'Critical',
|
||||
status: 'Open',
|
||||
details: 'SOFA delta >= 2',
|
||||
}
|
||||
|
||||
describe('AcknowledgeModal', () => {
|
||||
beforeEach(() => {
|
||||
setActivePinia(createPinia())
|
||||
const auth = useAuthStore()
|
||||
auth.$patch({
|
||||
user: {
|
||||
userId: '11111111-1111-1111-1111-111111111111',
|
||||
username: 'nurse.demo',
|
||||
displayName: 'Demo Nurse',
|
||||
role: 'NURSE',
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
it('showsAuthenticatedUserAndRoleContext', () => {
|
||||
const wrapper = mount(AcknowledgeModal, {
|
||||
props: { open: true, alert },
|
||||
global: {
|
||||
stubs: {
|
||||
Modal: {
|
||||
props: ['open', 'title'],
|
||||
template: '<div v-if="open"><slot /></div>',
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
expect(wrapper.text()).toContain('Demo Nurse')
|
||||
expect(wrapper.text()).toContain('Nurse')
|
||||
expect(wrapper.text()).toContain('documenting awareness')
|
||||
expect(wrapper.text()).toContain('[NURSE] Acknowledged by Demo Nurse.')
|
||||
})
|
||||
|
||||
it('emitsOptionalNoteOnConfirm', async () => {
|
||||
const wrapper = mount(AcknowledgeModal, {
|
||||
props: { open: true, alert },
|
||||
global: {
|
||||
stubs: {
|
||||
Modal: {
|
||||
props: ['open', 'title'],
|
||||
template: '<div v-if="open"><slot /></div>',
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
await wrapper.find('textarea').setValue('Will reassess in 30 minutes')
|
||||
const ackButton = wrapper.findAll('button').find(button => button.text() === 'Acknowledge')
|
||||
await ackButton.trigger('click')
|
||||
|
||||
expect(wrapper.emitted('confirm')).toEqual([['Will reassess in 30 minutes']])
|
||||
})
|
||||
})
|
||||
@@ -36,6 +36,20 @@ describe('AlertCard', () => {
|
||||
expect(wrapper.emitted('acknowledge')).toHaveLength(1)
|
||||
})
|
||||
|
||||
it('showsAcknowledgedByWhenPresent', () => {
|
||||
const wrapper = mount(AlertCard, {
|
||||
props: {
|
||||
alert: {
|
||||
...openAlert,
|
||||
status: 'Acknowledged',
|
||||
acknowledgedBy: 'Demo Nurse (NURSE)',
|
||||
acknowledgedAt: '2026-06-19T12:30:00Z',
|
||||
},
|
||||
},
|
||||
})
|
||||
expect(wrapper.text()).toContain('Acknowledged by Demo Nurse (Nurse)')
|
||||
})
|
||||
|
||||
it('resolvedAlertHidesActions', () => {
|
||||
const wrapper = mount(AlertCard, { props: { alert: resolvedAlert } })
|
||||
const actionButtons = wrapper.findAll('button').filter(b => ['Acknowledge', 'Resolve'].includes(b.text()))
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
import { describe, it, expect, vi } from 'vitest'
|
||||
import { mount } from '@vue/test-utils'
|
||||
import { createPinia, setActivePinia } from 'pinia'
|
||||
import CriticalAlertBanner from '@/components/alerts/CriticalAlertBanner.vue'
|
||||
import { useAlertStore } from '@/stores/alerts'
|
||||
|
||||
const { mockPush } = vi.hoisted(() => ({
|
||||
mockPush: vi.fn(),
|
||||
}))
|
||||
|
||||
vi.mock('vue-router', () => ({
|
||||
useRouter: () => ({ push: mockPush }),
|
||||
}))
|
||||
|
||||
describe('CriticalAlertBanner', () => {
|
||||
it('rendersCriticalAlertsAndDismisses', async () => {
|
||||
setActivePinia(createPinia())
|
||||
const alertStore = useAlertStore()
|
||||
alertStore.bannerAlerts = [{
|
||||
id: 'alert-1',
|
||||
alertType: 'SofaSepsis',
|
||||
severity: 'Critical',
|
||||
status: 'Open',
|
||||
details: 'SOFA delta >= 2',
|
||||
}]
|
||||
|
||||
const wrapper = mount(CriticalAlertBanner)
|
||||
expect(wrapper.text()).toContain('Sepsis Alert (SOFA)')
|
||||
expect(wrapper.text()).toContain('SOFA delta >= 2')
|
||||
|
||||
const dismissButton = wrapper.findAll('button').find(button => button.text() === 'Dismiss')
|
||||
await dismissButton.trigger('click')
|
||||
expect(alertStore.bannerAlerts).toEqual([])
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,54 @@
|
||||
import { describe, it, expect, vi } from 'vitest'
|
||||
import { mount } from '@vue/test-utils'
|
||||
import { createPinia, setActivePinia } from 'pinia'
|
||||
import DepartmentOverviewView from '@/views/DepartmentOverviewView.vue'
|
||||
|
||||
const { mockPush } = vi.hoisted(() => ({
|
||||
mockPush: vi.fn(),
|
||||
}))
|
||||
|
||||
vi.mock('vue-router', () => ({
|
||||
useRouter: () => ({ push: mockPush }),
|
||||
}))
|
||||
|
||||
vi.mock('@/composables/usePolling', () => ({
|
||||
usePolling: (fn) => {
|
||||
fn()
|
||||
},
|
||||
}))
|
||||
|
||||
vi.mock('@/api/encounters', () => ({
|
||||
fetchAllActiveEncounters: vi.fn(() => Promise.resolve([
|
||||
{
|
||||
department: 'ICU',
|
||||
news2Score: 8,
|
||||
openAlertCount: 2,
|
||||
sepsisActive: true,
|
||||
sepsisBundleStatus: 'IN_PROGRESS',
|
||||
},
|
||||
{
|
||||
department: 'GENERAL_MEDICINE',
|
||||
news2Score: 3,
|
||||
openAlertCount: 0,
|
||||
sepsisActive: false,
|
||||
},
|
||||
])),
|
||||
}))
|
||||
|
||||
vi.mock('@/api/analytics', () => ({
|
||||
fetchAlertSummary: vi.fn(() => Promise.resolve({
|
||||
summary: [{ department: 'ICU', total: 5 }],
|
||||
})),
|
||||
}))
|
||||
|
||||
describe('DepartmentOverviewView', () => {
|
||||
it('rendersDepartmentCardsAndTotals', async () => {
|
||||
setActivePinia(createPinia())
|
||||
const wrapper = mount(DepartmentOverviewView)
|
||||
await vi.waitFor(() => expect(wrapper.text()).toContain('1 active patient'))
|
||||
|
||||
expect(wrapper.text()).toContain('Department Overview')
|
||||
expect(wrapper.text()).toContain('Critical (NEWS2 ≥ 7)')
|
||||
expect(wrapper.text()).toContain('Alert volume')
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,49 @@
|
||||
import { describe, it, expect, vi } from 'vitest'
|
||||
import { mount } from '@vue/test-utils'
|
||||
import { createPinia, setActivePinia } from 'pinia'
|
||||
import SepsisBoardView from '@/views/SepsisBoardView.vue'
|
||||
|
||||
const { mockPush } = vi.hoisted(() => ({
|
||||
mockPush: vi.fn(),
|
||||
}))
|
||||
|
||||
vi.mock('vue-router', () => ({
|
||||
useRouter: () => ({ push: mockPush }),
|
||||
}))
|
||||
|
||||
vi.mock('@/composables/usePolling', () => ({
|
||||
usePolling: () => {},
|
||||
}))
|
||||
|
||||
const bundles = [
|
||||
{
|
||||
id: 'b1',
|
||||
encounterId: 'enc-1',
|
||||
firstName: 'Alice',
|
||||
lastName: 'A',
|
||||
mrn: 'M1',
|
||||
department: 'Icu',
|
||||
roomBed: '101',
|
||||
recognizedAt: '2026-06-23T12:00:00Z',
|
||||
deadlineAt: '2026-06-23T15:00:00Z',
|
||||
complianceStatus: 'IN_PROGRESS',
|
||||
elements: [
|
||||
{ id: 'e1', elementType: 'BloodCultures', status: 'Completed' },
|
||||
{ id: 'e2', elementType: 'SerumLactate', status: 'Pending' },
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
vi.mock('@/api/sepsis', () => ({
|
||||
fetchSepsisBundles: vi.fn(() => Promise.resolve({ items: bundles, totalCount: 1 })),
|
||||
}))
|
||||
|
||||
describe('SepsisBoardView', () => {
|
||||
it('rendersBundleBoardWithSummary', async () => {
|
||||
setActivePinia(createPinia())
|
||||
const wrapper = mount(SepsisBoardView)
|
||||
await vi.waitFor(() => expect(wrapper.text()).toContain('Alice A'))
|
||||
expect(wrapper.text()).toContain('Sepsis Bundle Board')
|
||||
expect(wrapper.text()).toContain('Serum lactate')
|
||||
})
|
||||
})
|
||||
@@ -68,14 +68,19 @@ const patients = [
|
||||
]
|
||||
|
||||
describe('WardTable', () => {
|
||||
const sortProps = {
|
||||
sortField: 'news2Score',
|
||||
sortDirection: 'desc',
|
||||
}
|
||||
|
||||
it('rendersAllPatientRows', () => {
|
||||
const wrapper = mount(WardTable, { props: { patients } })
|
||||
const wrapper = mount(WardTable, { props: { patients, ...sortProps } })
|
||||
expect(wrapper.findAll('tbody tr')).toHaveLength(3)
|
||||
})
|
||||
|
||||
it('emitsClickWithEncounterId', async () => {
|
||||
mockPush.mockClear()
|
||||
const wrapper = mount(WardTable, { props: { patients } })
|
||||
const wrapper = mount(WardTable, { props: { patients, ...sortProps } })
|
||||
await wrapper.findAll('tbody tr')[2].trigger('click')
|
||||
expect(mockPush).toHaveBeenCalledWith({
|
||||
name: 'PatientDetail',
|
||||
@@ -84,18 +89,26 @@ describe('WardTable', () => {
|
||||
})
|
||||
|
||||
it('showsCriticalBadgeForHighNews2', () => {
|
||||
const wrapper = mount(WardTable, { props: { patients } })
|
||||
const wrapper = mount(WardTable, { props: { patients, ...sortProps } })
|
||||
const highRiskRow = wrapper.findAll('tbody tr')[2]
|
||||
expect(highRiskRow.html()).toContain('text-severity-critical')
|
||||
})
|
||||
|
||||
it('showsExtendedClinicalColumns', () => {
|
||||
const wrapper = mount(WardTable, { props: { patients } })
|
||||
const wrapper = mount(WardTable, { props: { patients, ...sortProps } })
|
||||
const header = wrapper.find('thead').text()
|
||||
expect(header).toContain('SOFA')
|
||||
expect(header).toContain('GCS')
|
||||
expect(header).toContain('Department')
|
||||
expect(header).toContain('Last vitals')
|
||||
expect(wrapper.text()).toContain('Dr. C')
|
||||
expect(wrapper.text()).toContain('Δ+2')
|
||||
})
|
||||
|
||||
it('emitsSortWhenHeaderClicked', async () => {
|
||||
const wrapper = mount(WardTable, { props: { patients, ...sortProps } })
|
||||
const roomHeader = wrapper.findAll('thead button').find(button => button.text().includes('Room'))
|
||||
await roomHeader.trigger('click')
|
||||
expect(wrapper.emitted('sort')).toEqual([['roomBed']])
|
||||
})
|
||||
})
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
import { describe, it, expect } from 'vitest'
|
||||
import {
|
||||
formatAcknowledgedByDisplay,
|
||||
previewAcknowledgmentNote,
|
||||
roleAcknowledgmentMessage,
|
||||
} from '@/composables/alertAcknowledge'
|
||||
|
||||
describe('alertAcknowledge', () => {
|
||||
it('buildsRoleAwareNotePreview', () => {
|
||||
expect(previewAcknowledgmentNote('NURSE', 'Demo Nurse', 'Escalating to physician'))
|
||||
.toBe('[NURSE] Acknowledged by Demo Nurse. Escalating to physician')
|
||||
})
|
||||
|
||||
it('usesRoleSpecificAcknowledgmentMessage', () => {
|
||||
expect(roleAcknowledgmentMessage('PHYSICIAN')).toContain('physician')
|
||||
})
|
||||
|
||||
it('formatsAcknowledgedByDisplay', () => {
|
||||
expect(formatAcknowledgedByDisplay('Demo Nurse (NURSE)')).toBe('Demo Nurse (Nurse)')
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,48 @@
|
||||
import { describe, it, expect } from 'vitest'
|
||||
import { detectNewCriticalAlerts, isNotifiableCriticalAlert } from '@/composables/criticalAlertDetect'
|
||||
|
||||
const criticalOpen = {
|
||||
id: 'a1',
|
||||
severity: 'Critical',
|
||||
status: 'Open',
|
||||
alertType: 'SofaSepsis',
|
||||
}
|
||||
|
||||
const criticalAcknowledged = {
|
||||
id: 'a2',
|
||||
severity: 'Critical',
|
||||
status: 'Acknowledged',
|
||||
alertType: 'News2Emergency',
|
||||
}
|
||||
|
||||
const warningOpen = {
|
||||
id: 'a3',
|
||||
severity: 'Warning',
|
||||
status: 'Open',
|
||||
alertType: 'News2Warning',
|
||||
}
|
||||
|
||||
describe('criticalAlertDetect', () => {
|
||||
it('identifiesNotifiableCriticalAlerts', () => {
|
||||
expect(isNotifiableCriticalAlert(criticalOpen)).toBe(true)
|
||||
expect(isNotifiableCriticalAlert(criticalAcknowledged)).toBe(false)
|
||||
expect(isNotifiableCriticalAlert(warningOpen)).toBe(false)
|
||||
})
|
||||
|
||||
it('seedsSeenIdsOnFirstPoll', () => {
|
||||
const result = detectNewCriticalAlerts([criticalOpen, warningOpen], [], false)
|
||||
expect(result.newAlerts).toEqual([])
|
||||
expect(result.nextSeenIds).toEqual(['a1'])
|
||||
expect(result.seeded).toBe(true)
|
||||
})
|
||||
|
||||
it('detectsNewCriticalAlertsAfterSeed', () => {
|
||||
const seeded = detectNewCriticalAlerts([criticalOpen], [], false)
|
||||
const next = detectNewCriticalAlerts(
|
||||
[criticalOpen, { ...criticalOpen, id: 'a4', alertType: 'GcsCritical' }],
|
||||
seeded.nextSeenIds,
|
||||
seeded.seeded,
|
||||
)
|
||||
expect(next.newAlerts.map(alert => alert.id)).toEqual(['a4'])
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,87 @@
|
||||
import { describe, it, expect } from 'vitest'
|
||||
import {
|
||||
aggregateByDepartment,
|
||||
news2AcuityTier,
|
||||
normalizeDepartmentKey,
|
||||
summarizeDepartments,
|
||||
} from '@/composables/departmentFormat'
|
||||
|
||||
const encounters = [
|
||||
{
|
||||
department: 'ICU',
|
||||
news2Score: 8,
|
||||
openAlertCount: 2,
|
||||
sepsisActive: true,
|
||||
sepsisBundleStatus: 'IN_PROGRESS',
|
||||
},
|
||||
{
|
||||
department: 'Icu',
|
||||
news2Score: 5,
|
||||
openAlertCount: 1,
|
||||
sepsisActive: false,
|
||||
sepsisBundleStatus: null,
|
||||
},
|
||||
{
|
||||
department: 'GENERAL_MEDICINE',
|
||||
news2Score: 2,
|
||||
openAlertCount: 0,
|
||||
sepsisActive: false,
|
||||
sepsisBundleStatus: null,
|
||||
},
|
||||
{
|
||||
department: 'Surgery',
|
||||
news2Score: 6,
|
||||
openAlertCount: 3,
|
||||
sepsisActive: true,
|
||||
sepsisBundleStatus: 'IN_PROGRESS',
|
||||
},
|
||||
]
|
||||
|
||||
describe('departmentFormat', () => {
|
||||
it('normalizesDepartmentKeys', () => {
|
||||
expect(normalizeDepartmentKey('Icu')).toBe('ICU')
|
||||
expect(normalizeDepartmentKey('GeneralMedicine')).toBe('GENERAL_MEDICINE')
|
||||
})
|
||||
|
||||
it('classifiesNews2Acuity', () => {
|
||||
expect(news2AcuityTier(2)).toBe('low')
|
||||
expect(news2AcuityTier(5)).toBe('medium')
|
||||
expect(news2AcuityTier(7)).toBe('high')
|
||||
})
|
||||
|
||||
it('aggregatesByDepartment', () => {
|
||||
const departments = aggregateByDepartment(encounters, [
|
||||
{ department: 'ICU', total: 10 },
|
||||
{ department: 'SURGERY', total: 4 },
|
||||
])
|
||||
|
||||
const icu = departments.find(d => d.key === 'ICU')
|
||||
const surgery = departments.find(d => d.key === 'SURGERY')
|
||||
const general = departments.find(d => d.key === 'GENERAL_MEDICINE')
|
||||
|
||||
expect(icu.patientCount).toBe(2)
|
||||
expect(icu.acuity.high).toBe(1)
|
||||
expect(icu.acuity.medium).toBe(1)
|
||||
expect(icu.activeBundleCount).toBe(1)
|
||||
expect(icu.openAlertCount).toBe(3)
|
||||
expect(icu.averageNews2).toBe(6.5)
|
||||
expect(icu.alertVolume).toBe(10)
|
||||
|
||||
expect(surgery.patientCount).toBe(1)
|
||||
expect(surgery.activeBundleCount).toBe(1)
|
||||
expect(surgery.alertVolume).toBe(4)
|
||||
|
||||
expect(general.patientCount).toBe(1)
|
||||
expect(general.acuity.low).toBe(1)
|
||||
})
|
||||
|
||||
it('summarizesTotals', () => {
|
||||
const departments = aggregateByDepartment(encounters)
|
||||
expect(summarizeDepartments(departments)).toEqual({
|
||||
patientCount: 4,
|
||||
criticalCount: 1,
|
||||
activeBundleCount: 2,
|
||||
openAlertCount: 6,
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,56 @@
|
||||
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
|
||||
import {
|
||||
bundleUrgency,
|
||||
formatCountdown,
|
||||
sortBundlesByUrgency,
|
||||
summarizeBundles,
|
||||
} from '@/composables/sepsisFormat'
|
||||
|
||||
const baseBundle = {
|
||||
complianceStatus: 'IN_PROGRESS',
|
||||
deadlineAt: '2026-06-23T15:00:00Z',
|
||||
}
|
||||
|
||||
describe('sepsisFormat', () => {
|
||||
const now = new Date('2026-06-23T14:00:00Z').getTime()
|
||||
|
||||
beforeEach(() => {
|
||||
vi.useFakeTimers()
|
||||
vi.setSystemTime(now)
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
vi.useRealTimers()
|
||||
})
|
||||
|
||||
it('classifiesUrgency', () => {
|
||||
expect(bundleUrgency({ ...baseBundle, deadlineAt: '2026-06-23T15:00:00Z' }, now)).toBe('on_track')
|
||||
expect(bundleUrgency({ ...baseBundle, deadlineAt: '2026-06-23T14:20:00Z' }, now)).toBe('at_risk')
|
||||
expect(bundleUrgency({ ...baseBundle, deadlineAt: '2026-06-23T13:00:00Z' }, now)).toBe('overdue')
|
||||
expect(bundleUrgency({ complianceStatus: 'NON_COMPLIANT', deadlineAt: '2026-06-23T15:00:00Z' }, now)).toBe('overdue')
|
||||
})
|
||||
|
||||
it('formatsCountdown', () => {
|
||||
expect(formatCountdown('2026-06-23T14:45:00Z', now)).toBe('45:00')
|
||||
expect(formatCountdown('2026-06-23T13:00:00Z', now)).toBe('0:00')
|
||||
})
|
||||
|
||||
it('sortsByUrgency', () => {
|
||||
const bundles = [
|
||||
{ id: 'a', complianceStatus: 'IN_PROGRESS', deadlineAt: '2026-06-23T15:00:00Z' },
|
||||
{ id: 'b', complianceStatus: 'IN_PROGRESS', deadlineAt: '2026-06-23T13:00:00Z' },
|
||||
{ id: 'c', complianceStatus: 'IN_PROGRESS', deadlineAt: '2026-06-23T14:20:00Z' },
|
||||
]
|
||||
const sorted = sortBundlesByUrgency(bundles, now)
|
||||
expect(sorted.map(b => b.id)).toEqual(['b', 'c', 'a'])
|
||||
})
|
||||
|
||||
it('summarizesBundles', () => {
|
||||
const bundles = [
|
||||
{ complianceStatus: 'IN_PROGRESS', deadlineAt: '2026-06-23T15:00:00Z' },
|
||||
{ complianceStatus: 'IN_PROGRESS', deadlineAt: '2026-06-23T14:20:00Z' },
|
||||
{ complianceStatus: 'IN_PROGRESS', deadlineAt: '2026-06-23T13:00:00Z' },
|
||||
]
|
||||
expect(summarizeBundles(bundles, now)).toEqual({ on_track: 1, at_risk: 1, overdue: 1 })
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,39 @@
|
||||
import { describe, it, expect, beforeEach, vi } from 'vitest'
|
||||
import { createPinia, setActivePinia } from 'pinia'
|
||||
import { useAlertStore } from '@/stores/alerts'
|
||||
|
||||
describe('alert store notifications', () => {
|
||||
beforeEach(() => {
|
||||
setActivePinia(createPinia())
|
||||
})
|
||||
|
||||
it('queuesBannerAlertsForNewCriticalItems', () => {
|
||||
const store = useAlertStore()
|
||||
const critical = {
|
||||
id: 'alert-1',
|
||||
severity: 'Critical',
|
||||
status: 'Open',
|
||||
alertType: 'SofaSepsis',
|
||||
}
|
||||
|
||||
expect(store.applyPollResults([critical])).toEqual([])
|
||||
expect(store.bannerAlerts).toEqual([])
|
||||
|
||||
const next = {
|
||||
id: 'alert-2',
|
||||
severity: 'Critical',
|
||||
status: 'Open',
|
||||
alertType: 'GcsCritical',
|
||||
}
|
||||
const newAlerts = store.applyPollResults([critical, next])
|
||||
expect(newAlerts.map(alert => alert.id)).toEqual(['alert-2'])
|
||||
expect(store.bannerAlerts.map(alert => alert.id)).toEqual(['alert-2'])
|
||||
})
|
||||
|
||||
it('dismissesBannerAlert', () => {
|
||||
const store = useAlertStore()
|
||||
store.bannerAlerts = [{ id: 'alert-1' }]
|
||||
store.dismissBannerAlert('alert-1')
|
||||
expect(store.bannerAlerts).toEqual([])
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,49 @@
|
||||
import { describe, it, expect, beforeEach } from 'vitest'
|
||||
import { createPinia, setActivePinia } from 'pinia'
|
||||
import { useSettingsStore } from '@/stores/settings'
|
||||
import { useWardStore } from '@/stores/ward'
|
||||
|
||||
describe('ward store sort', () => {
|
||||
beforeEach(() => {
|
||||
localStorage.clear()
|
||||
setActivePinia(createPinia())
|
||||
})
|
||||
|
||||
it('persistsSortPreference', () => {
|
||||
const settings = useSettingsStore()
|
||||
const ward = useWardStore()
|
||||
|
||||
ward.setSort('name')
|
||||
expect(settings.wardSortField).toBe('name')
|
||||
expect(settings.wardSortDirection).toBe('asc')
|
||||
expect(localStorage.getItem('wardSortField')).toBe('name')
|
||||
|
||||
ward.setSort('name')
|
||||
expect(settings.wardSortDirection).toBe('desc')
|
||||
expect(localStorage.getItem('wardSortDirection')).toBe('desc')
|
||||
})
|
||||
})
|
||||
|
||||
describe('ward store filters', () => {
|
||||
beforeEach(() => {
|
||||
localStorage.clear()
|
||||
setActivePinia(createPinia())
|
||||
})
|
||||
|
||||
it('filtersEncountersForDisplay', () => {
|
||||
const ward = useWardStore()
|
||||
ward.encounters = [
|
||||
{ encounterId: '1', firstName: 'Alice', lastName: 'A', mrn: 'M1', news2Score: 8, openAlertCount: 1 },
|
||||
{ encounterId: '2', firstName: 'Bob', lastName: 'B', mrn: 'M2', news2Score: 3, openAlertCount: 0 },
|
||||
]
|
||||
|
||||
ward.toggleFilter('critical')
|
||||
expect(ward.displayEncounters.map(e => e.encounterId)).toEqual(['1'])
|
||||
|
||||
ward.toggleFilter('hasAlerts')
|
||||
expect(ward.displayEncounters.map(e => e.encounterId)).toEqual(['1'])
|
||||
|
||||
ward.clearFilters()
|
||||
expect(ward.displayEncounters.map(e => e.encounterId)).toEqual(['1', '2'])
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,72 @@
|
||||
import { describe, it, expect } from 'vitest'
|
||||
import { filterEncounters, matchesPatientSearch } from '@/composables/wardFilter'
|
||||
|
||||
const patients = [
|
||||
{
|
||||
encounterId: '1',
|
||||
firstName: 'Alice',
|
||||
lastName: 'Anderson',
|
||||
mrn: 'MRN-100',
|
||||
news2Score: 8,
|
||||
openAlertCount: 2,
|
||||
sepsisActive: true,
|
||||
sepsisBundleStatus: 'IN_PROGRESS',
|
||||
},
|
||||
{
|
||||
encounterId: '2',
|
||||
firstName: 'Bob',
|
||||
lastName: 'Baker',
|
||||
mrn: 'MRN-200',
|
||||
news2Score: 4,
|
||||
openAlertCount: 0,
|
||||
sepsisActive: false,
|
||||
},
|
||||
{
|
||||
encounterId: '3',
|
||||
firstName: 'Carol',
|
||||
lastName: 'Clark',
|
||||
mrn: 'MRN-300',
|
||||
news2Score: 7,
|
||||
openAlertCount: 1,
|
||||
sepsisActive: false,
|
||||
sepsisBundleStatus: 'IN_PROGRESS',
|
||||
},
|
||||
]
|
||||
|
||||
describe('wardFilter', () => {
|
||||
it('matchesNameOrMrn', () => {
|
||||
expect(matchesPatientSearch(patients[0], 'alice')).toBe(true)
|
||||
expect(matchesPatientSearch(patients[0], 'MRN-100')).toBe(true)
|
||||
expect(matchesPatientSearch(patients[0], 'anderson')).toBe(true)
|
||||
expect(matchesPatientSearch(patients[0], 'xyz')).toBe(false)
|
||||
})
|
||||
|
||||
it('filtersBySearch', () => {
|
||||
const result = filterEncounters(patients, { search: 'baker' })
|
||||
expect(result.map(p => p.encounterId)).toEqual(['2'])
|
||||
})
|
||||
|
||||
it('filtersByHasAlerts', () => {
|
||||
const result = filterEncounters(patients, { hasAlerts: true })
|
||||
expect(result.map(p => p.encounterId)).toEqual(['1', '3'])
|
||||
})
|
||||
|
||||
it('filtersBySepsisActive', () => {
|
||||
const result = filterEncounters(patients, { sepsisActive: true })
|
||||
expect(result.map(p => p.encounterId)).toEqual(['1', '3'])
|
||||
})
|
||||
|
||||
it('filtersByCriticalNews2', () => {
|
||||
const result = filterEncounters(patients, { critical: true })
|
||||
expect(result.map(p => p.encounterId)).toEqual(['1', '3'])
|
||||
})
|
||||
|
||||
it('combinesSearchAndFilters', () => {
|
||||
const result = filterEncounters(patients, {
|
||||
search: 'carol',
|
||||
hasAlerts: true,
|
||||
critical: true,
|
||||
})
|
||||
expect(result.map(p => p.encounterId)).toEqual(['3'])
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,78 @@
|
||||
import { describe, it, expect, beforeEach } from 'vitest'
|
||||
import {
|
||||
defaultSortDirection,
|
||||
sortEncounters,
|
||||
} from '@/composables/wardSort'
|
||||
|
||||
const patients = [
|
||||
{
|
||||
encounterId: '1',
|
||||
firstName: 'Bob',
|
||||
lastName: 'B',
|
||||
roomBed: '202',
|
||||
department: 'SURGERY',
|
||||
news2Score: 5,
|
||||
qsofaScore: 1,
|
||||
sepsisActive: false,
|
||||
openAlertCount: 1,
|
||||
},
|
||||
{
|
||||
encounterId: '2',
|
||||
firstName: 'Alice',
|
||||
lastName: 'A',
|
||||
roomBed: '101',
|
||||
department: 'ICU',
|
||||
news2Score: 8,
|
||||
qsofaScore: 2,
|
||||
sepsisActive: true,
|
||||
sepsisBundleStatus: 'IN_PROGRESS',
|
||||
openAlertCount: 3,
|
||||
},
|
||||
{
|
||||
encounterId: '3',
|
||||
firstName: 'Carol',
|
||||
lastName: 'C',
|
||||
roomBed: '103',
|
||||
department: 'GENERAL_MEDICINE',
|
||||
news2Score: 2,
|
||||
qsofaScore: 0,
|
||||
sepsisActive: false,
|
||||
openAlertCount: 0,
|
||||
},
|
||||
]
|
||||
|
||||
describe('wardSort', () => {
|
||||
beforeEach(() => {
|
||||
localStorage.clear()
|
||||
})
|
||||
|
||||
it('defaultsTextFieldsToAscending', () => {
|
||||
expect(defaultSortDirection('name')).toBe('asc')
|
||||
expect(defaultSortDirection('news2Score')).toBe('desc')
|
||||
})
|
||||
|
||||
it('sortsByNews2DescendingByDefault', () => {
|
||||
const sorted = sortEncounters(patients, 'news2Score', 'desc')
|
||||
expect(sorted.map(p => p.encounterId)).toEqual(['2', '1', '3'])
|
||||
})
|
||||
|
||||
it('sortsByPatientNameAscending', () => {
|
||||
const sorted = sortEncounters(patients, 'name', 'asc')
|
||||
expect(sorted.map(p => p.firstName)).toEqual(['Alice', 'Bob', 'Carol'])
|
||||
})
|
||||
|
||||
it('sortsByRoomBedAscending', () => {
|
||||
const sorted = sortEncounters(patients, 'roomBed', 'asc')
|
||||
expect(sorted.map(p => p.roomBed)).toEqual(['101', '103', '202'])
|
||||
})
|
||||
|
||||
it('sortsByOpenAlertCountDescending', () => {
|
||||
const sorted = sortEncounters(patients, 'openAlertCount', 'desc')
|
||||
expect(sorted.map(p => p.encounterId)).toEqual(['2', '1', '3'])
|
||||
})
|
||||
|
||||
it('sortsBySepsisStatusDescending', () => {
|
||||
const sorted = sortEncounters(patients, 'sepsis', 'desc')
|
||||
expect(sorted[0].encounterId).toBe('2')
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user