From 7ba2e8ea6ffb20b71b9781e27c07e9f6a4d26ac7 Mon Sep 17 00:00:00 2001 From: Jack Meyer Date: Sun, 19 Apr 2020 01:18:47 -0500 Subject: [PATCH] feat(labs): reduxify request, cancel, fetch, and update labs --- src/__tests__/labs/Labs.test.tsx | 14 + src/__tests__/labs/ViewLab.test.tsx | 55 ++- src/__tests__/labs/ViewLabs.test.tsx | 2 +- src/__tests__/labs/lab-slice.test.ts | 374 ++++++++++++++++++ .../labs/requests/NewLabRequest.test.tsx | 106 ++--- src/labs/ViewLab.tsx | 2 - src/labs/lab-slice.ts | 9 +- 7 files changed, 460 insertions(+), 102 deletions(-) create mode 100644 src/__tests__/labs/lab-slice.test.ts diff --git a/src/__tests__/labs/Labs.test.tsx b/src/__tests__/labs/Labs.test.tsx index 82d13bf0c7..03fddb7284 100644 --- a/src/__tests__/labs/Labs.test.tsx +++ b/src/__tests__/labs/Labs.test.tsx @@ -34,6 +34,11 @@ describe('Labs', () => { user: { permissions: [Permissions.RequestLab] }, breadcrumbs: { breadcrumbs: [] }, components: { sidebarCollapsed: false }, + lab: { + lab: { id: 'labId', patientId: 'patientId' } as Lab, + patient: { id: 'patientId', fullName: 'some name' }, + error: {}, + }, }) const wrapper = mount( @@ -74,6 +79,15 @@ describe('Labs', () => { user: { permissions: [Permissions.ViewLab] }, breadcrumbs: { breadcrumbs: [] }, components: { sidebarCollapsed: false }, + lab: { + lab: { + id: 'labId', + patientId: 'patientId', + requestedOn: new Date().toISOString(), + } as Lab, + patient: { id: 'patientId', fullName: 'some name' }, + error: {}, + }, }) let wrapper: any diff --git a/src/__tests__/labs/ViewLab.test.tsx b/src/__tests__/labs/ViewLab.test.tsx index 8bcd97476e..97f17faff5 100644 --- a/src/__tests__/labs/ViewLab.test.tsx +++ b/src/__tests__/labs/ViewLab.test.tsx @@ -37,7 +37,7 @@ describe('View Labs', () => { let titleSpy: any let labRepositorySaveSpy: any const expectedDate = new Date() - const setup = async (lab: Lab, permissions: Permissions[]) => { + const setup = async (lab: Lab, permissions: Permissions[], error = {}) => { jest.resetAllMocks() Date.now = jest.fn(() => expectedDate.valueOf()) setButtonToolBarSpy = jest.fn() @@ -54,6 +54,12 @@ describe('View Labs', () => { user: { permissions, }, + lab: { + lab, + patient: mockPatient, + error, + status: Object.keys(error).length > 0 ? 'error' : 'success', + }, }) let wrapper: any @@ -133,11 +139,11 @@ describe('View Labs', () => { } as Lab const wrapper = await setup(expectedLab, [Permissions.ViewLab]) - const notesTextField = wrapper.find(TextFieldWithLabelFormGroup).at(0) + const resultTextField = wrapper.find(TextFieldWithLabelFormGroup).at(0) - expect(notesTextField).toBeDefined() - expect(notesTextField.prop('label')).toEqual('labs.lab.result') - expect(notesTextField.prop('value')).toEqual(expectedLab.result) + expect(resultTextField).toBeDefined() + expect(resultTextField.prop('label')).toEqual('labs.lab.result') + expect(resultTextField.prop('value')).toEqual(expectedLab.result) }) it('should display the notes in the notes text field', async () => { @@ -151,6 +157,20 @@ describe('View Labs', () => { expect(notesTextField.prop('value')).toEqual(expectedLab.notes) }) + it('should display errors', async () => { + const expectedLab = { ...mockLab, status: 'requested' } as Lab + const expectedError = { message: 'some message', result: 'some result feedback' } + const wrapper = await setup(expectedLab, [Permissions.ViewLab], expectedError) + + const alert = wrapper.find(Alert) + const resultTextField = wrapper.find(TextFieldWithLabelFormGroup).at(0) + expect(alert.prop('message')).toEqual(expectedError.message) + expect(alert.prop('title')).toEqual('states.error') + expect(alert.prop('color')).toEqual('danger') + expect(resultTextField.prop('isInvalid')).toBeTruthy() + expect(resultTextField.prop('feedback')).toEqual(expectedError.result) + }) + describe('requested lab request', () => { it('should display a warning badge if the status is requested', async () => { const expectedLab = { ...mockLab, status: 'requested' } as Lab @@ -343,31 +363,6 @@ describe('View Labs', () => { ) expect(history.location.pathname).toEqual('/labs') }) - - it('should validate that the result has been filled in', async () => { - const wrapper = await setup(mockLab, [ - Permissions.ViewLab, - Permissions.CompleteLab, - Permissions.CancelLab, - ]) - - const completeButton = wrapper.find(Button).at(1) - await act(async () => { - const onClick = completeButton.prop('onClick') - await onClick() - }) - wrapper.update() - - const alert = wrapper.find(Alert) - const resultField = wrapper.find(TextFieldWithLabelFormGroup).at(0) - expect(alert).toHaveLength(1) - expect(alert.prop('title')).toEqual('states.error') - expect(alert.prop('message')).toEqual('labs.requests.error.unableToComplete') - expect(resultField.prop('isInvalid')).toBeTruthy() - expect(resultField.prop('feedback')).toEqual('labs.requests.error.resultRequiredToComplete') - - expect(labRepositorySaveSpy).not.toHaveBeenCalled() - }) }) describe('on cancel', () => { diff --git a/src/__tests__/labs/ViewLabs.test.tsx b/src/__tests__/labs/ViewLabs.test.tsx index 8a3e2fb266..abc149ad1e 100644 --- a/src/__tests__/labs/ViewLabs.test.tsx +++ b/src/__tests__/labs/ViewLabs.test.tsx @@ -38,7 +38,7 @@ describe('View Labs', () => { }) }) - it('should have New Lab Request as the title', () => { + it('should have the title', () => { expect(titleSpy).toHaveBeenCalledWith('labs.label') }) }) diff --git a/src/__tests__/labs/lab-slice.test.ts b/src/__tests__/labs/lab-slice.test.ts new file mode 100644 index 0000000000..cad18ea0a5 --- /dev/null +++ b/src/__tests__/labs/lab-slice.test.ts @@ -0,0 +1,374 @@ +import thunk from 'redux-thunk' +import createMockStore from 'redux-mock-store' +import PatientRepository from '../../clients/db/PatientRepository' +import LabRepository from '../../clients/db/LabRepository' +import Lab from '../../model/Lab' +import Patient from '../../model/Patient' +import * as labSlice from '../../labs/lab-slice' +import { RootState } from '../../store' +import { requestLab } from '../../labs/lab-slice' + +const mockStore = createMockStore([thunk]) + +describe('lab slice', () => { + describe('reducers', () => { + describe('fetchLabStart', () => { + it('should set status to loading', async () => { + const labStore = labSlice.default(undefined, labSlice.fetchLabStart()) + + expect(labStore.status).toEqual('loading') + }) + }) + + describe('fetchLabSuccess', () => { + it('should set the lab, patient, and status to success', () => { + const expectedLab = { id: 'labId' } as Lab + const expectedPatient = { id: 'patientId' } as Patient + + const labStore = labSlice.default( + undefined, + labSlice.fetchLabSuccess({ lab: expectedLab, patient: expectedPatient }), + ) + + expect(labStore.status).toEqual('success') + expect(labStore.lab).toEqual(expectedLab) + expect(labStore.patient).toEqual(expectedPatient) + }) + }) + + describe('updateLabStart', () => { + it('should set status to loading', async () => { + const labStore = labSlice.default(undefined, labSlice.updateLabStart()) + + expect(labStore.status).toEqual('loading') + }) + }) + + describe('updateLabSuccess', () => { + it('should set the lab and status to success', () => { + const expectedLab = { id: 'labId' } as Lab + + const labStore = labSlice.default(undefined, labSlice.updateLabSuccess(expectedLab)) + + expect(labStore.status).toEqual('success') + expect(labStore.lab).toEqual(expectedLab) + }) + }) + + describe('requestLabStart', () => { + it('should set status to loading', async () => { + const labStore = labSlice.default(undefined, labSlice.requestLabStart()) + + expect(labStore.status).toEqual('loading') + }) + }) + + describe('requestLabSuccess', () => { + it('should set the lab and status to success', () => { + const expectedLab = { id: 'labId' } as Lab + + const labStore = labSlice.default(undefined, labSlice.requestLabSuccess(expectedLab)) + + expect(labStore.status).toEqual('success') + expect(labStore.lab).toEqual(expectedLab) + }) + }) + + describe('requestLabError', () => { + const expectedError = { message: 'some message', result: 'some result error' } + + const labStore = labSlice.default(undefined, labSlice.requestLabError(expectedError)) + + expect(labStore.status).toEqual('error') + expect(labStore.error).toEqual(expectedError) + }) + + describe('completeLabStart', () => { + it('should set status to loading', async () => { + const labStore = labSlice.default(undefined, labSlice.completeLabStart()) + + expect(labStore.status).toEqual('loading') + }) + }) + + describe('completeLabSuccess', () => { + it('should set the lab and status to success', () => { + const expectedLab = { id: 'labId' } as Lab + + const labStore = labSlice.default(undefined, labSlice.completeLabSuccess(expectedLab)) + + expect(labStore.status).toEqual('success') + expect(labStore.lab).toEqual(expectedLab) + }) + }) + + describe('completeLabError', () => { + const expectedError = { message: 'some message', result: 'some result error' } + + const labStore = labSlice.default(undefined, labSlice.completeLabError(expectedError)) + + expect(labStore.status).toEqual('error') + expect(labStore.error).toEqual(expectedError) + }) + + describe('cancelLabStart', () => { + it('should set status to loading', async () => { + const labStore = labSlice.default(undefined, labSlice.cancelLabStart()) + + expect(labStore.status).toEqual('loading') + }) + }) + + describe('cancelLabSuccess', () => { + it('should set the lab and status to success', () => { + const expectedLab = { id: 'labId' } as Lab + + const labStore = labSlice.default(undefined, labSlice.cancelLabSuccess(expectedLab)) + + expect(labStore.status).toEqual('success') + expect(labStore.lab).toEqual(expectedLab) + }) + }) + }) + + describe('fetch lab', () => { + let patientRepositorySpy: any + let labRepositoryFindSpy: any + + const mockLab = { + id: 'labId', + patientId: 'patientId', + } as Lab + + const mockPatient = { + id: 'patientId', + } as Patient + + beforeEach(() => { + patientRepositorySpy = jest.spyOn(PatientRepository, 'find').mockResolvedValue(mockPatient) + labRepositoryFindSpy = jest.spyOn(LabRepository, 'find').mockResolvedValue(mockLab) + }) + + it('should fetch the lab and patient', async () => { + const store = mockStore() + + await store.dispatch(labSlice.fetchLab(mockLab.id)) + const actions = store.getActions() + + expect(actions[0]).toEqual(labSlice.fetchLabStart()) + expect(labRepositoryFindSpy).toHaveBeenCalledWith(mockLab.id) + expect(patientRepositorySpy).toHaveBeenCalledWith(mockLab.patientId) + expect(actions[1]).toEqual(labSlice.fetchLabSuccess({ lab: mockLab, patient: mockPatient })) + }) + }) + + describe('cancel lab', () => { + const mockLab = { + id: 'labId', + patientId: 'patientId', + } as Lab + let labRepositorySaveOrUpdateSpy: any + + beforeEach(() => { + Date.now = jest.fn().mockReturnValue(new Date().valueOf()) + labRepositorySaveOrUpdateSpy = jest + .spyOn(LabRepository, 'saveOrUpdate') + .mockResolvedValue(mockLab) + }) + + it('should cancel a lab', async () => { + const expectedCanceledLab = { + ...mockLab, + canceledOn: new Date(Date.now()).toISOString(), + status: 'canceled', + } as Lab + + const store = mockStore() + + await store.dispatch(labSlice.cancelLab(mockLab)) + const actions = store.getActions() + + expect(actions[0]).toEqual(labSlice.cancelLabStart()) + expect(labRepositorySaveOrUpdateSpy).toHaveBeenCalledWith(expectedCanceledLab) + expect(actions[1]).toEqual(labSlice.cancelLabSuccess(expectedCanceledLab)) + }) + + it('should call on success callback if provided', async () => { + const expectedCanceledLab = { + ...mockLab, + canceledOn: new Date(Date.now()).toISOString(), + status: 'canceled', + } as Lab + + const store = mockStore() + const onSuccessSpy = jest.fn() + await store.dispatch(labSlice.cancelLab(mockLab, onSuccessSpy)) + + expect(onSuccessSpy).toHaveBeenCalledWith(expectedCanceledLab) + }) + }) + + describe('complete lab', () => { + const mockLab = { + id: 'labId', + patientId: 'patientId', + result: 'lab result', + } as Lab + let labRepositorySaveOrUpdateSpy: any + + beforeEach(() => { + Date.now = jest.fn().mockReturnValue(new Date().valueOf()) + labRepositorySaveOrUpdateSpy = jest + .spyOn(LabRepository, 'saveOrUpdate') + .mockResolvedValue(mockLab) + }) + + it('should complete a lab', async () => { + const expectedCompletedLab = { + ...mockLab, + completedOn: new Date(Date.now()).toISOString(), + status: 'completed', + result: 'lab result', + } as Lab + + const store = mockStore() + + await store.dispatch(labSlice.completeLab(mockLab)) + const actions = store.getActions() + + expect(actions[0]).toEqual(labSlice.completeLabStart()) + expect(labRepositorySaveOrUpdateSpy).toHaveBeenCalledWith(expectedCompletedLab) + expect(actions[1]).toEqual(labSlice.completeLabSuccess(expectedCompletedLab)) + }) + + it('should call on success callback if provided', async () => { + const expectedCompletedLab = { + ...mockLab, + completedOn: new Date(Date.now()).toISOString(), + status: 'completed', + } as Lab + + const store = mockStore() + const onSuccessSpy = jest.fn() + await store.dispatch(labSlice.completeLab(mockLab, onSuccessSpy)) + + expect(onSuccessSpy).toHaveBeenCalledWith(expectedCompletedLab) + }) + + it('should validate that the lab can be completed', async () => { + const store = mockStore() + const onSuccessSpy = jest.fn() + await store.dispatch(labSlice.completeLab({ id: 'labId' } as Lab, onSuccessSpy)) + const actions = store.getActions() + + expect(actions[1]).toEqual( + labSlice.completeLabError({ + result: 'labs.requests.error.resultRequiredToComplete', + message: 'labs.requests.error.unableToComplete', + }), + ) + expect(onSuccessSpy).not.toHaveBeenCalled() + }) + }) + + describe('request lab', () => { + const mockLab = { + id: 'labId', + type: 'labType', + patientId: 'patientId', + } as Lab + let labRepositorySaveSpy: any + + beforeEach(() => { + jest.restoreAllMocks() + Date.now = jest.fn().mockReturnValue(new Date().valueOf()) + labRepositorySaveSpy = jest.spyOn(LabRepository, 'save').mockResolvedValue(mockLab) + }) + + it('should request a new lab', async () => { + const store = mockStore() + const expectedRequestedLab = { + ...mockLab, + requestedOn: new Date(Date.now()).toISOString(), + status: 'requested', + } as Lab + + await store.dispatch(requestLab(mockLab)) + + const actions = store.getActions() + + expect(actions[0]).toEqual(labSlice.requestLabStart()) + expect(labRepositorySaveSpy).toHaveBeenCalledWith(expectedRequestedLab) + expect(actions[1]).toEqual(labSlice.requestLabSuccess(expectedRequestedLab)) + }) + + it('should execute the onSuccess callback if provided', async () => { + const store = mockStore() + const onSuccessSpy = jest.fn() + + await store.dispatch(requestLab(mockLab, onSuccessSpy)) + + expect(onSuccessSpy).toHaveBeenCalledWith(mockLab) + }) + + it('should validate that the lab can be requested', async () => { + const store = mockStore() + const onSuccessSpy = jest.fn() + await store.dispatch(requestLab({} as Lab, onSuccessSpy)) + + const actions = store.getActions() + + expect(actions[0]).toEqual(labSlice.requestLabStart()) + expect(actions[1]).toEqual( + labSlice.requestLabError({ + patient: 'labs.requests.error.patientRequired', + type: 'labs.requests.error.typeRequired', + message: 'labs.requests.error.unableToRequest', + }), + ) + expect(labRepositorySaveSpy).not.toHaveBeenCalled() + expect(onSuccessSpy).not.toHaveBeenCalled() + }) + }) + + describe('update lab', () => { + const mockLab = { + id: 'labId', + patientId: 'patientId', + result: 'lab result', + } as Lab + let labRepositorySaveOrUpdateSpy: any + + const expectedUpdatedLab = { + ...mockLab, + type: 'some other type', + } + + beforeEach(() => { + Date.now = jest.fn().mockReturnValue(new Date().valueOf()) + labRepositorySaveOrUpdateSpy = jest + .spyOn(LabRepository, 'saveOrUpdate') + .mockResolvedValue(expectedUpdatedLab) + }) + + it('should update the lab', async () => { + const store = mockStore() + + await store.dispatch(labSlice.updateLab(expectedUpdatedLab)) + const actions = store.getActions() + + expect(actions[0]).toEqual(labSlice.updateLabStart()) + expect(labRepositorySaveOrUpdateSpy).toHaveBeenCalledWith(expectedUpdatedLab) + expect(actions[1]).toEqual(labSlice.updateLabSuccess(expectedUpdatedLab)) + }) + + it('should call the onSuccess callback if successful', async () => { + const store = mockStore() + const onSuccessSpy = jest.fn() + + await store.dispatch(labSlice.updateLab(expectedUpdatedLab, onSuccessSpy)) + + expect(onSuccessSpy).toHaveBeenCalledWith(expectedUpdatedLab) + }) + }) +}) diff --git a/src/__tests__/labs/requests/NewLabRequest.test.tsx b/src/__tests__/labs/requests/NewLabRequest.test.tsx index 87575dbc8e..7e280b0e58 100644 --- a/src/__tests__/labs/requests/NewLabRequest.test.tsx +++ b/src/__tests__/labs/requests/NewLabRequest.test.tsx @@ -25,7 +25,7 @@ describe('New Lab Request', () => { const history = createMemoryHistory() beforeEach(() => { - const store = mockStore({ title: '' }) + const store = mockStore({ title: '', lab: { status: 'loading', error: {} } }) titleSpy = jest.spyOn(titleUtil, 'default') history.push('/labs/new') @@ -48,7 +48,7 @@ describe('New Lab Request', () => { const history = createMemoryHistory() beforeEach(() => { - const store = mockStore({ title: '' }) + const store = mockStore({ title: '', lab: { status: 'loading', error: {} } }) history.push('/labs/new') wrapper = mount( @@ -106,13 +106,50 @@ describe('New Lab Request', () => { }) }) + describe('errors', () => { + let wrapper: ReactWrapper + const history = createMemoryHistory() + const error = { + message: 'some message', + patient: 'some patient message', + type: 'some type error', + } + + beforeEach(() => { + history.push('/labs/new') + const store = mockStore({ title: '', lab: { status: 'error', error } }) + wrapper = mount( + + + + + , + ) + }) + + it('should display errors', () => { + const alert = wrapper.find(Alert) + const typeInput = wrapper.find(TextInputWithLabelFormGroup) + const patientTypeahead = wrapper.find(Typeahead) + + expect(alert.prop('message')).toEqual(error.message) + expect(alert.prop('title')).toEqual('states.error') + expect(alert.prop('color')).toEqual('danger') + + expect(patientTypeahead.prop('isInvalid')).toBeTruthy() + + expect(typeInput.prop('feedback')).toEqual(error.type) + expect(typeInput.prop('isInvalid')).toBeTruthy() + }) + }) + describe('on cancel', () => { let wrapper: ReactWrapper const history = createMemoryHistory() beforeEach(() => { history.push('/labs/new') - const store = mockStore({ title: '' }) + const store = mockStore({ title: '', lab: { status: 'loading', error: {} } }) wrapper = mount( @@ -158,7 +195,7 @@ describe('New Lab Request', () => { .mockResolvedValue([{ id: expectedLab.patientId, fullName: 'some full name' }] as Patient[]) history.push('/labs/new') - const store = mockStore({ title: '' }) + const store = mockStore({ title: '', lab: { status: 'loading', error: {} } }) wrapper = mount( @@ -206,66 +243,5 @@ describe('New Lab Request', () => { ) expect(history.location.pathname).toEqual(`/labs/${expectedLab.id}`) }) - - it('should require a patient', async () => { - const typeInput = wrapper.find(TextInputWithLabelFormGroup) - act(() => { - const onChange = typeInput.prop('onChange') as any - onChange({ currentTarget: { value: expectedLab.type } }) - }) - - const notesTextField = wrapper.find(TextFieldWithLabelFormGroup) - act(() => { - const onChange = notesTextField.prop('onChange') as any - onChange({ currentTarget: { value: expectedLab.notes } }) - }) - wrapper.update() - - const saveButton = wrapper.find(Button).at(0) - await act(async () => { - const onClick = saveButton.prop('onClick') as any - await onClick() - }) - wrapper.update() - - const alert = wrapper.find(Alert) - const typeahead = wrapper.find(Typeahead) - expect(alert).toBeDefined() - expect(alert.prop('title')).toEqual('states.error') - expect(alert.prop('message')).toEqual('labs.requests.error.unableToRequest') - expect(typeahead.prop('isInvalid')).toBeTruthy() - expect(labRepositorySaveSpy).not.toHaveBeenCalled() - }) - - it('should require a type', async () => { - const patientTypeahead = wrapper.find(Typeahead) - await act(async () => { - const onChange = patientTypeahead.prop('onChange') - await onChange([{ id: expectedLab.patientId }] as Patient[]) - }) - - const notesTextField = wrapper.find(TextFieldWithLabelFormGroup) - act(() => { - const onChange = notesTextField.prop('onChange') as any - onChange({ currentTarget: { value: expectedLab.notes } }) - }) - wrapper.update() - - const saveButton = wrapper.find(Button).at(0) - await act(async () => { - const onClick = saveButton.prop('onClick') as any - await onClick() - }) - wrapper.update() - - const alert = wrapper.find(Alert) - const typeInput = wrapper.find(TextInputWithLabelFormGroup) - expect(alert).toBeDefined() - expect(alert.prop('title')).toEqual('states.error') - expect(alert.prop('message')).toEqual('labs.requests.error.unableToRequest') - expect(typeInput.prop('isInvalid')).toBeTruthy() - expect(typeInput.prop('feedback')).toEqual('labs.requests.error.typeRequired') - expect(labRepositorySaveSpy).not.toHaveBeenCalled() - }) }) }) diff --git a/src/labs/ViewLab.tsx b/src/labs/ViewLab.tsx index b4d8e2d52e..96ea16e96d 100644 --- a/src/labs/ViewLab.tsx +++ b/src/labs/ViewLab.tsx @@ -48,8 +48,6 @@ const ViewLab = () => { setLabToView({ ...lab }) setIsEditable(lab.status === 'requested') } - console.log('lab change') - console.log(lab) }, [lab]) const onResultChange = (event: React.ChangeEvent) => { diff --git a/src/labs/lab-slice.ts b/src/labs/lab-slice.ts index 5e4b26be3f..e4e8b880d5 100644 --- a/src/labs/lab-slice.ts +++ b/src/labs/lab-slice.ts @@ -112,12 +112,13 @@ export const requestLab = (newLab: Lab, onSuccess?: (lab: Lab) => void): AppThun labRequestError.message = 'labs.requests.error.unableToRequest' dispatch(requestLabError(labRequestError)) } else { + newLab.status = 'requested' newLab.requestedOn = new Date(Date.now().valueOf()).toISOString() - const requestedLab = await LabRepository.saveOrUpdate(newLab) + const requestedLab = await LabRepository.save(newLab) dispatch(requestLabSuccess(requestedLab)) if (onSuccess) { - onSuccess(newLab) + onSuccess(requestedLab) } } } @@ -150,7 +151,7 @@ const validateCompleteLab = (labToComplete: Lab): Error => { export const completeLab = (labToComplete: Lab, onSuccess?: (lab: Lab) => void): AppThunk => async ( dispatch, ) => { - dispatch(cancelLabStart()) + dispatch(completeLabStart()) const completeLabErrors = validateCompleteLab(labToComplete) if (Object.keys(completeLabErrors).length > 0) { @@ -160,7 +161,7 @@ export const completeLab = (labToComplete: Lab, onSuccess?: (lab: Lab) => void): labToComplete.completedOn = new Date(Date.now().valueOf()).toISOString() labToComplete.status = 'completed' const completedLab = await LabRepository.saveOrUpdate(labToComplete) - dispatch(cancelLabSuccess(completedLab)) + dispatch(completeLabSuccess(completedLab)) if (onSuccess) { onSuccess(completedLab)