Skip to content
This repository has been archived by the owner on Jan 9, 2023. It is now read-only.

feat(medications): implement basic medication module #2249

Merged
merged 43 commits into from
Aug 14, 2020
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
e97c3ca
fix: add Relates Person search crash if matched person has no DoB
blestab Jul 8, 2020
5a34b8f
feat(incidents): add ability to resolve an incident
blestab Jul 10, 2020
daada44
Merge branch 'master' into master
matteovivona Jul 10, 2020
77c2be5
Merge branch 'master' into master
fox1t Jul 10, 2020
0b4d018
Merge branch 'master' into master
fox1t Jul 10, 2020
d2b935e
Merge branch 'master' of https://github.com/blestab/hospitalrun-frontend
blestab Jul 10, 2020
d43fa37
Merge branch 'master' of https://github.com/blestab/hospitalrun-frontend
blestab Jul 10, 2020
bfecf24
Merge branch 'master' of https://github.com/blestab/hospitalrun-frontend
blestab Jul 10, 2020
6f6bfe0
Merge branch 'master' into master
fox1t Jul 10, 2020
52dc151
Merge branch 'master' into master
matteovivona Jul 11, 2020
6bea698
Merge branch 'master' into master
matteovivona Jul 11, 2020
14ad01a
Merge branch 'master' of https://github.com/blestab/hospitalrun-frontend
blestab Jul 11, 2020
bfe9ccc
Merge branch 'master' into master
matteovivona Jul 11, 2020
2b01be8
Merge branch 'master' of https://github.com/blestab/hospitalrun-frontend
blestab Jul 11, 2020
20c0a71
Merge branch 'master' of https://github.com/blestab/hospitalrun-frontend
blestab Jul 11, 2020
eb9f2b3
Merge branch 'master' of https://github.com/blestab/hospitalrun-frontend
blestab Jul 11, 2020
309a716
Merge branch 'master' of https://github.com/blestab/hospitalrun-frontend
blestab Jul 11, 2020
a7e415c
feat(medications): implement basic medication module
blestab Jul 23, 2020
92e9233
Merge branch 'master' into master
matteovivona Jul 24, 2020
6944fb3
feat(medications): implement basic medication module
blestab Jul 23, 2020
ac914fe
Merge branch 'master' of https://github.com/blestab/hospitalrun-frontend
blestab Jul 25, 2020
7074b90
Merge branch 'master' of https://github.com/blestab/hospitalrun-frontend
blestab Jul 25, 2020
65ef8cb
Merge branch 'master' of https://github.com/blestab/hospitalrun-frontend
blestab Jul 26, 2020
d950e50
feat(medications): implement basic medication module
blestab Jul 26, 2020
40b08d8
Merge branch 'master' into master
matteovivona Jul 27, 2020
cae8481
feat(navbar): add "Request Medication" to quick create button in navbar
blestab Jul 28, 2020
0de73fc
Merge branch 'master' of https://github.com/blestab/hospitalrun-frontend
blestab Jul 28, 2020
abebbc7
Merge branch 'master' into master
matteovivona Jul 30, 2020
ea93a61
Merge branch 'master' into master
matteovivona Jul 31, 2020
cef43eb
Merge branch 'master' into master
matteovivona Aug 2, 2020
ee02c91
feat(medications): PR Feedback (tests, remove 'complete' feature)
blestab Aug 2, 2020
4a06814
Merge branch 'master' of https://github.com/blestab/hospitalrun-frontend
blestab Aug 2, 2020
6e909c4
feat(medications): PR feedback - refactor tests, setup function
blestab Aug 3, 2020
b67f68f
feat(medications): pr feedback refactor tests add setup function
blestab Aug 3, 2020
45678a0
Merge branch 'master' of https://github.com/blestab/hospitalrun-frontend
blestab Aug 3, 2020
5cf1307
Merge branch 'master' into fix_merge_conflicts_imagings_meds
blestab Aug 13, 2020
e52802d
fix: merge conflict imaging and medications
blestab Aug 13, 2020
871f1e7
fix(medications): pr merge conflicts
blestab Aug 13, 2020
c19fbac
fix merge issues
blestab Aug 13, 2020
07a9ff3
fix merge issues
blestab Aug 13, 2020
a755035
fix merge issues
blestab Aug 13, 2020
5e72dce
fix merge issues sidebar
blestab Aug 13, 2020
e2a83a2
bug: fix sidebar test
morrme Aug 13, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/HospitalRun.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { Redirect, Route, Switch } from 'react-router-dom'
import Dashboard from './dashboard/Dashboard'
import Incidents from './incidents/Incidents'
import Labs from './labs/Labs'
import Medications from './medications/Medications'
import Breadcrumbs from './page-header/breadcrumbs/Breadcrumbs'
import { ButtonBarProvider } from './page-header/button-toolbar/ButtonBarProvider'
import ButtonToolBar from './page-header/button-toolbar/ButtonToolBar'
Expand Down Expand Up @@ -51,6 +52,7 @@ const HospitalRun = () => {
<Route path="/appointments" component={Appointments} />
<Route path="/patients" component={Patients} />
<Route path="/labs" component={Labs} />
<Route path="/medications" component={Medications} />
<Route path="/incidents" component={Incidents} />
<Route path="/settings" component={Settings} />
</Switch>
Expand Down
64 changes: 64 additions & 0 deletions src/__tests__/incidents/incident-slice.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ import incident, {
fetchIncidentStart,
fetchIncidentSuccess,
fetchIncident,
resolveIncident,
resolveIncidentStart,
resolveIncidentSuccess,
} from '../../incidents/incident-slice'
import IncidentRepository from '../../shared/db/IncidentRepository'
import Incident from '../../shared/model/Incident'
Expand Down Expand Up @@ -70,6 +73,11 @@ describe('incident slice', () => {
expect(incidentStore.status).toEqual('loading')
})

it('should handle resolve incident start', () => {
const incidentStore = incident(undefined, resolveIncidentStart())
expect(incidentStore.status).toEqual('loading')
})

it('should handle fetch incident success', () => {
const expectedIncident = {
id: '1234',
Expand All @@ -80,6 +88,18 @@ describe('incident slice', () => {
expect(incidentStore.status).toEqual('completed')
expect(incidentStore.incident).toEqual(expectedIncident)
})

it('should handle resolve incident success', () => {
const expectedIncident = {
id: '1234',
resolvedOn: new Date(Date.now()).toISOString(),
status: 'resolved',
} as Incident

const incidentStore = incident(undefined, resolveIncidentSuccess(expectedIncident))
expect(incidentStore.status).toEqual('completed')
expect(incidentStore.incident).toEqual(expectedIncident)
})
})

describe('report incident', () => {
Expand Down Expand Up @@ -202,4 +222,48 @@ describe('incident slice', () => {
expect(store.getActions()[1]).toEqual(fetchIncidentSuccess(expectedIncident))
})
})

describe('resolve incident', () => {
const expectedDate = new Date()
const mockIncident = {
id: '123',
description: 'description',
date: expectedDate.toISOString(),
department: 'some department',
category: 'category',
categoryItem: 'categoryItem',
status: 'reported',
} as Incident
const expectedResolvedIncident = {
...mockIncident,
resolvedOn: expectedDate.toISOString(),
status: 'resolved',
} as Incident
let incidentRepositorySaveOrUpdateSpy: any

beforeEach(() => {
Date.now = jest.fn().mockReturnValue(expectedDate.valueOf())
incidentRepositorySaveOrUpdateSpy = jest
.spyOn(IncidentRepository, 'saveOrUpdate')
.mockResolvedValue(expectedResolvedIncident)
})

it('should resolve an incident', async () => {
const store = mockStore()

await store.dispatch(resolveIncident(mockIncident))

expect(store.getActions()[0]).toEqual(resolveIncidentStart())
expect(incidentRepositorySaveOrUpdateSpy).toHaveBeenCalledWith(expectedResolvedIncident)
expect(store.getActions()[1]).toEqual(resolveIncidentSuccess(expectedResolvedIncident))
})

it('should call on success callback if provided', async () => {
const store = mockStore()
const onSuccessSpy = jest.fn()
await store.dispatch(resolveIncident(mockIncident, onSuccessSpy))

expect(onSuccessSpy).toHaveBeenCalledWith(expectedResolvedIncident)
})
})
})
102 changes: 90 additions & 12 deletions src/__tests__/incidents/view/ViewIncident.test.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Button } from '@hospitalrun/components'
import { act } from '@testing-library/react'
import { mount } from 'enzyme'
import { createMemoryHistory } from 'history'
Expand All @@ -20,6 +21,8 @@ const mockStore = createMockStore<RootState, any>([thunk])

describe('View Incident', () => {
const expectedDate = new Date(2020, 5, 1, 19, 48)
const expectedResolveDate = new Date()
let incidentRepositorySaveSpy: any
let history: any
const expectedIncident = {
id: '1234',
Expand All @@ -34,11 +37,15 @@ describe('View Incident', () => {
date: expectedDate.toISOString(),
} as Incident

const setup = async (permissions: Permissions[]) => {
const setup = async (mockIncident: Incident, permissions: Permissions[]) => {
jest.resetAllMocks()
Date.now = jest.fn(() => expectedResolveDate.valueOf())
jest.spyOn(breadcrumbUtil, 'default')
jest.spyOn(titleUtil, 'default')
jest.spyOn(IncidentRepository, 'find').mockResolvedValue(expectedIncident)
incidentRepositorySaveSpy = jest
.spyOn(IncidentRepository, 'saveOrUpdate')
.mockResolvedValue(expectedIncident)

history = createMemoryHistory()
history.push(`/incidents/1234`)
Expand All @@ -49,7 +56,7 @@ describe('View Incident', () => {
permissions,
},
incident: {
incident: expectedIncident,
incident: mockIncident,
},
} as any)

Expand All @@ -73,81 +80,152 @@ describe('View Incident', () => {

describe('layout', () => {
it('should set the title', async () => {
await setup([Permissions.ViewIncident])
await setup(expectedIncident, [Permissions.ViewIncident])

expect(titleUtil.default).toHaveBeenCalledWith(expectedIncident.code)
})

it('should set the breadcrumbs properly', async () => {
await setup([Permissions.ViewIncident])
await setup(expectedIncident, [Permissions.ViewIncident])

expect(breadcrumbUtil.default).toHaveBeenCalledWith([
{ i18nKey: expectedIncident.code, location: '/incidents/1234' },
])
})

it('should render the date of incident', async () => {
const wrapper = await setup([Permissions.ViewIncident])
const wrapper = await setup(expectedIncident, [Permissions.ViewIncident])

const dateOfIncidentFormGroup = wrapper.find('.incident-date')
expect(dateOfIncidentFormGroup.find('h4').text()).toEqual('incidents.reports.dateOfIncident')
expect(dateOfIncidentFormGroup.find('h5').text()).toEqual('2020-06-01 07:48 PM')
})

it('should render the status', async () => {
const wrapper = await setup([Permissions.ViewIncident])
const wrapper = await setup(expectedIncident, [Permissions.ViewIncident])

const dateOfIncidentFormGroup = wrapper.find('.incident-status')
expect(dateOfIncidentFormGroup.find('h4').text()).toEqual('incidents.reports.status')
expect(dateOfIncidentFormGroup.find('h5').text()).toEqual(expectedIncident.status)
})

it('should render the reported by', async () => {
const wrapper = await setup([Permissions.ViewIncident])
const wrapper = await setup(expectedIncident, [Permissions.ViewIncident])

const dateOfIncidentFormGroup = wrapper.find('.incident-reported-by')
expect(dateOfIncidentFormGroup.find('h4').text()).toEqual('incidents.reports.reportedBy')
expect(dateOfIncidentFormGroup.find('h5').text()).toEqual(expectedIncident.reportedBy)
})

it('should render the reported on', async () => {
const wrapper = await setup([Permissions.ViewIncident])
const wrapper = await setup(expectedIncident, [Permissions.ViewIncident])

const dateOfIncidentFormGroup = wrapper.find('.incident-reported-on')
expect(dateOfIncidentFormGroup.find('h4').text()).toEqual('incidents.reports.reportedOn')
expect(dateOfIncidentFormGroup.find('h5').text()).toEqual('2020-06-01 07:48 PM')
})

it('should render the resolved on if incident status is resolved', async () => {
const mockIncident = {
...expectedIncident,
status: 'resolved',
resolvedOn: '2020-07-10 06:33 PM',
} as Incident
const wrapper = await setup(mockIncident, [Permissions.ViewIncident])

const dateOfResolutionFormGroup = wrapper.find('.incident-resolved-on')
expect(dateOfResolutionFormGroup.find('h4').text()).toEqual('incidents.reports.resolvedOn')
expect(dateOfResolutionFormGroup.find('h5').text()).toEqual('2020-07-10 06:33 PM')
})

it('should not render the resolved on if incident status is not resolved', async () => {
const wrapper = await setup(expectedIncident, [Permissions.ViewIncident])

const completedOn = wrapper.find('.incident-resolved-on')
expect(completedOn).toHaveLength(0)
})

it('should render the department', async () => {
const wrapper = await setup([Permissions.ViewIncident])
const wrapper = await setup(expectedIncident, [Permissions.ViewIncident])

const departmentInput = wrapper.findWhere((w: any) => w.prop('name') === 'department')
expect(departmentInput.prop('label')).toEqual('incidents.reports.department')
expect(departmentInput.prop('value')).toEqual(expectedIncident.department)
})

it('should render the category', async () => {
const wrapper = await setup([Permissions.ViewIncident])
const wrapper = await setup(expectedIncident, [Permissions.ViewIncident])

const categoryInput = wrapper.findWhere((w: any) => w.prop('name') === 'category')
expect(categoryInput.prop('label')).toEqual('incidents.reports.category')
expect(categoryInput.prop('value')).toEqual(expectedIncident.category)
})

it('should render the category item', async () => {
const wrapper = await setup([Permissions.ViewIncident])
const wrapper = await setup(expectedIncident, [Permissions.ViewIncident])

const categoryItemInput = wrapper.findWhere((w: any) => w.prop('name') === 'categoryItem')
expect(categoryItemInput.prop('label')).toEqual('incidents.reports.categoryItem')
expect(categoryItemInput.prop('value')).toEqual(expectedIncident.categoryItem)
})

it('should render the description', async () => {
const wrapper = await setup([Permissions.ViewIncident])
const wrapper = await setup(expectedIncident, [Permissions.ViewIncident])

const descriptionTextInput = wrapper.findWhere((w: any) => w.prop('name') === 'description')
expect(descriptionTextInput.prop('label')).toEqual('incidents.reports.description')
expect(descriptionTextInput.prop('value')).toEqual(expectedIncident.description)
})

it('should display a resolve incident button if the incident is in a reported state', async () => {
const wrapper = await setup(expectedIncident, [
Permissions.ViewIncident,
Permissions.ResolveIncident,
])

const buttons = wrapper.find(Button)
expect(buttons.at(0).text().trim()).toEqual('incidents.reports.resolve')
})

it('should not display a resolve incident button if the user has no access ResolveIncident access', async () => {
const wrapper = await setup(expectedIncident, [Permissions.ViewIncident])

const resolveButton = wrapper.find(Button)
expect(resolveButton).toHaveLength(0)
})

it('should not display a resolve incident button if the incident is resolved', async () => {
const mockIncident = { ...expectedIncident, status: 'resolved' } as Incident
const wrapper = await setup(mockIncident, [Permissions.ViewIncident])

const resolveButton = wrapper.find(Button)
expect(resolveButton).toHaveLength(0)
})
})

describe('on resolve', () => {
it('should mark the status as resolved and fill in the resolved date with the current time', async () => {
const wrapper = await setup(expectedIncident, [
Permissions.ViewIncident,
Permissions.ResolveIncident,
])

const resolveButton = wrapper.find(Button).at(0)
await act(async () => {
const onClick = resolveButton.prop('onClick')
await onClick()
})
wrapper.update()

expect(incidentRepositorySaveSpy).toHaveBeenCalledTimes(1)
expect(incidentRepositorySaveSpy).toHaveBeenCalledWith(
expect.objectContaining({
...expectedIncident,
status: 'resolved',
resolvedOn: expectedResolveDate.toISOString(),
}),
)
expect(history.location.pathname).toEqual('/incidents')
})
})
})
13 changes: 13 additions & 0 deletions src/__tests__/shared/utils/extractUsername.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { extractUsername } from '../../../shared/util/extractUsername'

describe('extract username util', () => {
it('should extract the string after the last : in a given string', () => {
const extractedName = extractUsername('org.couchdb.user:username')
expect(extractedName).toMatch('username')
})

it('should return the string if string does not contain a : ', () => {
const extractedName = extractUsername('username')
expect(extractedName).toMatch('username')
})
})
1 change: 1 addition & 0 deletions src/incidents/IncidentFilter.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
enum IncidentFilter {
reported = 'reported',
resolved = 'resolved',
all = 'all',
}

Expand Down
23 changes: 23 additions & 0 deletions src/incidents/incident-slice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ const incidentSlice = createSlice({
reportIncidentStart: start,
reportIncidentSuccess: finish,
reportIncidentError: error,
resolveIncidentStart: start,
resolveIncidentSuccess: finish,
},
})

Expand All @@ -60,6 +62,8 @@ export const {
reportIncidentStart,
reportIncidentSuccess,
reportIncidentError,
resolveIncidentStart,
resolveIncidentSuccess,
} = incidentSlice.actions

export const fetchIncident = (id: string): AppThunk => async (dispatch) => {
Expand Down Expand Up @@ -120,4 +124,23 @@ export const reportIncident = (
}
}

export const resolveIncident = (
incidentToComplete: Incident,
onSuccess?: (incidentToComplete: Incident) => void,
): AppThunk => async (dispatch) => {
dispatch(resolveIncidentStart())

const resolvedIncident = await IncidentRepository.saveOrUpdate({
...incidentToComplete,
resolvedOn: new Date(Date.now().valueOf()).toISOString(),
status: 'resolved',
})

dispatch(resolveIncidentSuccess(resolvedIncident))

if (onSuccess) {
onSuccess(resolvedIncident)
}
}

export default incidentSlice.reducer