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

Commit

Permalink
feat(appointments): add callbacks for appointment actions
Browse files Browse the repository at this point in the history
  • Loading branch information
jackcmeyer committed Mar 5, 2020
1 parent 73677e2 commit c077d75
Show file tree
Hide file tree
Showing 8 changed files with 159 additions and 67 deletions.
78 changes: 35 additions & 43 deletions src/__tests__/scheduling/appointments/appointment-slice.test.ts
Expand Up @@ -135,7 +135,7 @@ describe('appointment slice', () => {
reason: 'reason',
} as Appointment

await createAppointment(expectedAppointment, createMemoryHistory())(dispatch, getState, null)
await createAppointment(expectedAppointment)(dispatch, getState, null)

expect(dispatch).toHaveBeenCalledWith({ type: createAppointmentStart.type })
})
Expand All @@ -155,18 +155,21 @@ describe('appointment slice', () => {
reason: 'reason',
} as Appointment

await createAppointment(expectedAppointment, createMemoryHistory())(dispatch, getState, null)
await createAppointment(expectedAppointment)(dispatch, getState, null)

expect(appointmentRepositorySaveSpy).toHaveBeenCalled()
expect(appointmentRepositorySaveSpy).toHaveBeenCalledWith(expectedAppointment)
})

it('should navigate the /appointments when an appointment is successfully created', async () => {
it('should call the onSuccess function', async () => {
const onSuccessSpy = jest.fn()
jest.spyOn(AppointmentRepository, 'save')
mocked(AppointmentRepository, true).save.mockResolvedValue({ id: '123' } as Appointment)
const expectedSavedAppointment = { id: '123' }
mocked(AppointmentRepository, true).save.mockResolvedValue(
expectedSavedAppointment as Appointment,
)
const dispatch = jest.fn()
const getState = jest.fn()
const history = createMemoryHistory()

const expectedAppointment = {
patientId: '123',
Expand All @@ -177,9 +180,9 @@ describe('appointment slice', () => {
reason: 'reason',
} as Appointment

await createAppointment(expectedAppointment, history)(dispatch, getState, null)
await createAppointment(expectedAppointment, onSuccessSpy)(dispatch, getState, null)

expect(history.location.pathname).toEqual('/appointments')
expect(onSuccessSpy).toHaveBeenCalledWith(expectedSavedAppointment)
})
})

Expand Down Expand Up @@ -247,22 +250,16 @@ describe('appointment slice', () => {

describe('deleteAppointment()', () => {
let deleteAppointmentSpy = jest.spyOn(AppointmentRepository, 'delete')
let toastSpy = jest.spyOn(components, 'Toast')
beforeEach(() => {
jest.resetAllMocks()
deleteAppointmentSpy = jest.spyOn(AppointmentRepository, 'delete')
toastSpy = jest.spyOn(components, 'Toast')
})

it('should dispatch the DELETE_APPOINTMENT_START action', async () => {
const dispatch = jest.fn()
const getState = jest.fn()

await deleteAppointment({ id: 'test1' } as Appointment, createMemoryHistory())(
dispatch,
getState,
null,
)
await deleteAppointment({ id: 'test1' } as Appointment)(dispatch, getState, null)

expect(dispatch).toHaveBeenCalledWith({ type: deleteAppointmentStart.type })
})
Expand All @@ -273,51 +270,31 @@ describe('appointment slice', () => {
const dispatch = jest.fn()
const getState = jest.fn()

await deleteAppointment(expectedAppointment, createMemoryHistory())(dispatch, getState, null)
await deleteAppointment(expectedAppointment)(dispatch, getState, null)

expect(deleteAppointmentSpy).toHaveBeenCalledTimes(1)
expect(deleteAppointmentSpy).toHaveBeenCalledWith(expectedAppointment)
})

it('should navigate to /appointments after deleting', async () => {
const history = createMemoryHistory()
const expectedAppointment = { id: 'appointmentId1' } as Appointment

it('should call the onSuccess function after successfully deleting', async () => {
const onSuccessSpy = jest.fn()
const dispatch = jest.fn()
const getState = jest.fn()

await deleteAppointment(expectedAppointment, history)(dispatch, getState, null)

expect(history.location.pathname).toEqual('/appointments')
})

it('should create a toast with a success message', async () => {
const dispatch = jest.fn()
const getState = jest.fn()

await deleteAppointment({ id: 'test1' } as Appointment, createMemoryHistory())(
await deleteAppointment({ id: 'test1' } as Appointment, onSuccessSpy)(
dispatch,
getState,
null,
)

expect(toastSpy).toHaveBeenCalledTimes(1)
expect(toastSpy).toHaveBeenLastCalledWith(
'success',
'states.success',
'scheduling.appointments.successfullyDeleted',
)
expect(onSuccessSpy).toHaveBeenCalled()
})

it('should dispatch the DELETE_APPOINTMENT_SUCCESS action', async () => {
const dispatch = jest.fn()
const getState = jest.fn()

await deleteAppointment({ id: 'test1' } as Appointment, createMemoryHistory())(
dispatch,
getState,
null,
)
await deleteAppointment({ id: 'test1' } as Appointment)(dispatch, getState, null)

expect(dispatch).toHaveBeenCalledWith({ type: deleteAppointmentSuccess.type })
})
Expand All @@ -333,7 +310,7 @@ describe('appointment slice', () => {
const mockedAppointmentRepository = mocked(AppointmentRepository, true)
mockedAppointmentRepository.saveOrUpdate.mockResolvedValue(expectedAppointment)

await updateAppointment(expectedAppointment, createMemoryHistory())(dispatch, getState, null)
await updateAppointment(expectedAppointment)(dispatch, getState, null)

expect(dispatch).toHaveBeenCalledWith({ type: updateAppointmentStart.type })
})
Expand All @@ -347,7 +324,7 @@ describe('appointment slice', () => {
const mockedAppointmentRepository = mocked(AppointmentRepository, true)
mockedAppointmentRepository.saveOrUpdate.mockResolvedValue(expectedAppointment)

await updateAppointment(expectedAppointment, createMemoryHistory())(dispatch, getState, null)
await updateAppointment(expectedAppointment)(dispatch, getState, null)

expect(AppointmentRepository.saveOrUpdate).toHaveBeenCalledWith(expectedAppointment)
})
Expand All @@ -361,12 +338,27 @@ describe('appointment slice', () => {
const mockedAppointmentRepository = mocked(AppointmentRepository, true)
mockedAppointmentRepository.saveOrUpdate.mockResolvedValue(expectedAppointment)

await updateAppointment(expectedAppointment, createMemoryHistory())(dispatch, getState, null)
await updateAppointment(expectedAppointment)(dispatch, getState, null)

expect(dispatch).toHaveBeenCalledWith({
type: updateAppointmentSuccess.type,
payload: expectedAppointment,
})
})

it('should call on the onSuccess function after successfully updating', async () => {
const onSuccessSpy = jest.fn()
const dispatch = jest.fn()
const getState = jest.fn()
jest.spyOn(AppointmentRepository, 'saveOrUpdate')
const expectedAppointmentId = 'sliceId11'
const expectedAppointment = { id: expectedAppointmentId } as Appointment
const mockedAppointmentRepository = mocked(AppointmentRepository, true)
mockedAppointmentRepository.saveOrUpdate.mockResolvedValue(expectedAppointment)

await updateAppointment(expectedAppointment, onSuccessSpy)(dispatch, getState, null)

expect(onSuccessSpy).toHaveBeenCalledWith(expectedAppointment)
})
})
})
Expand Up @@ -184,6 +184,24 @@ describe('Edit Appointment', () => {
)
})

it('should navigate to /appointments/:id when save is successful', async () => {
let wrapper: any
await act(async () => {
wrapper = await setup()
})

wrapper.update()

const saveButton = wrapper.find(Button).at(0)
const onClick = saveButton.prop('onClick') as any

await act(async () => {
await onClick()
})

expect(history.location.pathname).toEqual('/appointments/123')
})

it('should navigate to /appointments/:id when cancel is clicked', async () => {
let wrapper: any
await act(async () => {
Expand Down
Expand Up @@ -24,10 +24,13 @@ const mockStore = configureMockStore([thunk])
describe('New Appointment', () => {
let history: MemoryHistory
let store: MockStore
const expectedNewAppointment = { id: '123' }

const setup = () => {
jest.spyOn(AppointmentRepository, 'save')
mocked(AppointmentRepository, true).save.mockResolvedValue({ id: '123' } as Appointment)
mocked(AppointmentRepository, true).save.mockResolvedValue(
expectedNewAppointment as Appointment,
)

history = createMemoryHistory()
store = mockStore({
Expand Down Expand Up @@ -154,6 +157,41 @@ describe('New Appointment', () => {
expect(store.getActions()).toContainEqual(appointmentSlice.createAppointmentSuccess())
})

it('should navigate to /appointments/:id when a new appointment is created', async () => {
let wrapper: any
await act(async () => {
wrapper = await setup()
})

const expectedAppointment = {
patientId: '123',
startDateTime: roundToNearestMinutes(new Date(), { nearestTo: 15 }).toISOString(),
endDateTime: addMinutes(
roundToNearestMinutes(new Date(), { nearestTo: 15 }),
60,
).toISOString(),
location: 'location',
reason: 'reason',
type: 'type',
} as Appointment

act(() => {
const appointmentDetailForm = wrapper.find(AppointmentDetailForm)
const onFieldChange = appointmentDetailForm.prop('onFieldChange')
onFieldChange('patientId', expectedAppointment.patientId)
})
wrapper.update()
const saveButton = wrapper.find(Button).at(0)
expect(saveButton.text().trim()).toEqual('actions.save')
const onClick = saveButton.prop('onClick') as any

await act(async () => {
await onClick()
})

expect(history.location.pathname).toEqual(`/appointments/${expectedNewAppointment.id}`)
})

it('should display an error if there is no patient id', async () => {
let wrapper: any
await act(async () => {
Expand Down
Expand Up @@ -12,6 +12,7 @@ import AppointmentRepository from 'clients/db/AppointmentRepository'
import { mocked } from 'ts-jest/utils'
import { act } from 'react-dom/test-utils'
import { Spinner, Modal } from '@hospitalrun/components'
import * as components from '@hospitalrun/components'
import AppointmentDetailForm from 'scheduling/appointments/AppointmentDetailForm'
import PatientRepository from 'clients/db/PatientRepository'
import Patient from 'model/Patient'
Expand Down Expand Up @@ -261,5 +262,29 @@ describe('View Appointment', () => {
expect(store.getActions()).toContainEqual(appointmentSlice.deleteAppointmentStart())
expect(store.getActions()).toContainEqual(appointmentSlice.deleteAppointmentSuccess())
})

it('should navigate to /appointments and display a message when delete is successful', async () => {
jest.spyOn(components, 'Toast')
const mockedComponents = mocked(components, true)

let wrapper: any
await act(async () => {
wrapper = await setup(false, [Permissions.ReadAppointments, Permissions.DeleteAppointment])
})

const deleteConfirmationModal = wrapper.find(Modal)

await act(async () => {
await deleteConfirmationModal.prop('closeButton').onClick()
})
wrapper.update()

expect(history.location.pathname).toEqual('/appointments')
expect(mockedComponents.Toast).toHaveBeenCalledWith(
'success',
'states.success',
'scheduling.appointments.successfullyDeleted',
)
})
})
})
42 changes: 24 additions & 18 deletions src/scheduling/appointments/appointment-slice.ts
Expand Up @@ -71,36 +71,42 @@ export const fetchAppointment = (id: string): AppThunk => async (dispatch) => {
dispatch(fetchAppointmentSuccess({ appointment, patient }))
}

export const createAppointment = (appointment: Appointment, history: any): AppThunk => async (
dispatch,
) => {
export const createAppointment = (
appointment: Appointment,
onSuccess?: (appointment: Appointment) => void,
): AppThunk => async (dispatch) => {
dispatch(createAppointmentStart())
await AppointmentRepository.save(appointment)
const newAppointment = await AppointmentRepository.save(appointment)
dispatch(createAppointmentSuccess())
history.push('/appointments')
if (onSuccess) {
onSuccess(newAppointment)
}
}

export const updateAppointment = (appointment: Appointment, history: any): AppThunk => async (
dispatch,
) => {
export const updateAppointment = (
appointment: Appointment,
onSuccess?: (appointment: Appointment) => void,
): AppThunk => async (dispatch) => {
dispatch(updateAppointmentStart())
const updatedAppointment = await AppointmentRepository.saveOrUpdate(appointment)
dispatch(updateAppointmentSuccess(updatedAppointment))
history.push(`/appointments/${updatedAppointment.id}`)

if (onSuccess) {
onSuccess(updatedAppointment)
}
}

export const deleteAppointment = (appointment: Appointment, history: any): AppThunk => async (
dispatch,
) => {
export const deleteAppointment = (
appointment: Appointment,
onSuccess?: () => void,
): AppThunk => async (dispatch) => {
dispatch(deleteAppointmentStart())
await AppointmentRepository.delete(appointment)
history.push('/appointments')
Toast(
'success',
il8n.t('states.success'),
`${il8n.t('scheduling.appointments.successfullyDeleted')}`,
)
dispatch(deleteAppointmentSuccess())

if (onSuccess) {
onSuccess()
}
}

export default appointmentSlice.reducer
6 changes: 5 additions & 1 deletion src/scheduling/appointments/edit/EditAppointment.tsx
Expand Up @@ -52,6 +52,10 @@ const EditAppointment = () => {
history.push(`/appointments/${appointment.id}`)
}

const onSaveSuccess = () => {
history.push(`/appointments/${appointment.id}`)
}

const onSave = () => {
let newErrorMessage = ''
if (isBefore(new Date(appointment.endDateTime), new Date(appointment.startDateTime))) {
Expand All @@ -63,7 +67,7 @@ const EditAppointment = () => {
return
}

dispatch(updateAppointment(appointment as Appointment, history))
dispatch(updateAppointment(appointment as Appointment, onSaveSuccess))
}

const onFieldChange = (key: string, value: string | boolean) => {
Expand Down

0 comments on commit c077d75

Please sign in to comment.