diff --git a/src/__tests__/labs/Labs.test.tsx b/src/__tests__/labs/Labs.test.tsx index e8254db294..19077a05a5 100644 --- a/src/__tests__/labs/Labs.test.tsx +++ b/src/__tests__/labs/Labs.test.tsx @@ -1,3 +1,4 @@ +import { act } from '@testing-library/react' import { mount, ReactWrapper } from 'enzyme' import React from 'react' import { Provider } from 'react-redux' @@ -28,46 +29,39 @@ describe('Labs', () => { jest .spyOn(PatientRepository, 'find') .mockResolvedValue({ id: '12345', fullName: 'test test' } as Patient) - const setup = (route: string, permissions: Permissions[] = []) => { + + const setup = async (initialEntry: string, permissions: Permissions[] = []) => { const store = mockStore({ user: { permissions }, - breadcrumbs: { breadcrumbs: [] }, - components: { sidebarCollapsed: false }, - lab: { - lab: ({ - id: '1234', - patientId: 'patientId', - requestedOn: new Date().toISOString(), - } as unknown) as Lab, - patient: { id: 'patientId', fullName: 'some name' }, - error: {}, - }, } as any) - const wrapper = mount( - - - - - - - , - ) + let wrapper: any + await act(async () => { + wrapper = await mount( + + + + + + + , + ) + }) + wrapper.update() return { wrapper: wrapper as ReactWrapper } } describe('routing', () => { describe('/labs/new', () => { - it('should render the new lab request screen when /labs/new is accessed', () => { - const permissions: Permissions[] = [Permissions.RequestLab] - const { wrapper } = setup('/labs/new', permissions) + it('should render the new lab request screen when /labs/new is accessed', async () => { + const { wrapper } = await setup('/labs/new', [Permissions.RequestLab]) expect(wrapper.find(NewLabRequest)).toHaveLength(1) }) - it('should not navigate to /labs/new if the user does not have RequestLab permissions', () => { - const { wrapper } = setup('/labs/new') + it('should not navigate to /labs/new if the user does not have RequestLab permissions', async () => { + const { wrapper } = await setup('/labs/new') expect(wrapper.find(NewLabRequest)).toHaveLength(0) }) @@ -75,15 +69,14 @@ describe('Labs', () => { describe('/labs/:id', () => { it('should render the view lab screen when /labs/:id is accessed', async () => { - const permissions: Permissions[] = [Permissions.ViewLab] - const { wrapper } = setup('/labs/1234', permissions) + const { wrapper } = await setup('/labs/1234', [Permissions.ViewLab]) expect(wrapper.find(ViewLab)).toHaveLength(1) }) }) it('should not navigate to /labs/:id if the user does not have ViewLab permissions', async () => { - const { wrapper } = setup('/labs/1234') + const { wrapper } = await setup('/labs/1234') expect(wrapper.find(ViewLab)).toHaveLength(0) }) diff --git a/src/__tests__/labs/ViewLab.test.tsx b/src/__tests__/labs/ViewLab.test.tsx index baa9019ac5..42b9b1be3b 100644 --- a/src/__tests__/labs/ViewLab.test.tsx +++ b/src/__tests__/labs/ViewLab.test.tsx @@ -9,6 +9,8 @@ import { Router, Route } from 'react-router-dom' import createMockStore from 'redux-mock-store' import thunk from 'redux-thunk' +import * as validateUtil from '../../labs/utils/validate-lab' +import { LabError } from '../../labs/utils/validate-lab' import ViewLab from '../../labs/ViewLab' import * as ButtonBarProvider from '../../page-header/button-toolbar/ButtonBarProvider' import * as titleUtil from '../../page-header/title/TitleContext' @@ -38,15 +40,16 @@ describe('View Lab', () => { let setButtonToolBarSpy: any let labRepositorySaveSpy: any const expectedDate = new Date() + const setup = async (lab: Lab, permissions: Permissions[], error = {}) => { jest.resetAllMocks() Date.now = jest.fn(() => expectedDate.valueOf()) setButtonToolBarSpy = jest.fn() jest.spyOn(titleUtil, 'useUpdateTitle').mockImplementation(() => jest.fn()) jest.spyOn(ButtonBarProvider, 'useButtonToolbarSetter').mockReturnValue(setButtonToolBarSpy) - jest.spyOn(LabRepository, 'find').mockResolvedValue(lab) labRepositorySaveSpy = jest.spyOn(LabRepository, 'saveOrUpdate').mockResolvedValue(mockLab) jest.spyOn(PatientRepository, 'find').mockResolvedValue(mockPatient as Patient) + jest.spyOn(LabRepository, 'find').mockResolvedValue(lab) history = createMemoryHistory() history.push(`labs/${lab.id}`) @@ -80,7 +83,7 @@ describe('View Lab', () => { }) wrapper.find(ViewLab).props().updateTitle = jest.fn() wrapper.update() - return wrapper + return { wrapper: wrapper as ReactWrapper } } describe('title', () => { @@ -94,8 +97,9 @@ describe('View Lab', () => { describe('page content', () => { it('should display the patient full name for the for', async () => { const expectedLab = { ...mockLab } as Lab - const wrapper = await setup(expectedLab, [Permissions.ViewLab]) + const { wrapper } = await setup(expectedLab, [Permissions.ViewLab]) const forPatientDiv = wrapper.find('.for-patient') + expect(forPatientDiv.find('h4').text().trim()).toEqual('labs.lab.for') expect(forPatientDiv.find('h5').text().trim()).toEqual(mockPatient.fullName) @@ -103,7 +107,7 @@ describe('View Lab', () => { it('should display the lab type for type', async () => { const expectedLab = { ...mockLab, type: 'expected type' } as Lab - const wrapper = await setup(expectedLab, [Permissions.ViewLab]) + const { wrapper } = await setup(expectedLab, [Permissions.ViewLab]) const labTypeDiv = wrapper.find('.lab-type') expect(labTypeDiv.find('h4').text().trim()).toEqual('labs.lab.type') @@ -112,7 +116,7 @@ describe('View Lab', () => { it('should display the requested on date', async () => { const expectedLab = { ...mockLab, requestedOn: '2020-03-30T04:43:20.102Z' } as Lab - const wrapper = await setup(expectedLab, [Permissions.ViewLab]) + const { wrapper } = await setup(expectedLab, [Permissions.ViewLab]) const requestedOnDiv = wrapper.find('.requested-on') expect(requestedOnDiv.find('h4').text().trim()).toEqual('labs.lab.requestedOn') @@ -123,7 +127,7 @@ describe('View Lab', () => { it('should not display the completed date if the lab is not completed', async () => { const expectedLab = { ...mockLab } as Lab - const wrapper = await setup(expectedLab, [Permissions.ViewLab]) + const { wrapper } = await setup(expectedLab, [Permissions.ViewLab]) const completedOnDiv = wrapper.find('.completed-on') expect(completedOnDiv).toHaveLength(0) @@ -131,7 +135,7 @@ describe('View Lab', () => { it('should not display the canceled date if the lab is not canceled', async () => { const expectedLab = { ...mockLab } as Lab - const wrapper = await setup(expectedLab, [Permissions.ViewLab]) + const { wrapper } = await setup(expectedLab, [Permissions.ViewLab]) const completedOnDiv = wrapper.find('.canceled-on') expect(completedOnDiv).toHaveLength(0) @@ -142,7 +146,7 @@ describe('View Lab', () => { ...mockLab, result: 'expected results', } as Lab - const wrapper = await setup(expectedLab, [Permissions.ViewLab]) + const { wrapper } = await setup(expectedLab, [Permissions.ViewLab]) const resultTextField = wrapper.find(TextFieldWithLabelFormGroup).at(0) @@ -154,9 +158,9 @@ describe('View Lab', () => { it('should display the past notes', async () => { const expectedNotes = 'expected notes' const expectedLab = { ...mockLab, notes: [expectedNotes] } as Lab - const wrapper = await setup(expectedLab, [Permissions.ViewLab]) + const { wrapper } = await setup(expectedLab, [Permissions.ViewLab]) - const notes = wrapper.find('[data-test="note"]') + const notes = wrapper.findWhere((w) => w.prop('data-test') === 'note') const pastNotesIndex = notes.reduce( (result: number, item: ReactWrapper, index: number) => item.text().trim() === expectedNotes ? index : result, @@ -164,13 +168,22 @@ describe('View Lab', () => { ) expect(pastNotesIndex).not.toBe(-1) - expect(notes.length).toBe(1) + expect(notes).toHaveLength(1) + }) + + it('should not display past notes if there is not', async () => { + const expectedLab = { ...mockLab, notes: undefined } as Lab + const { wrapper } = await setup(expectedLab, [Permissions.ViewLab]) + + const notes = wrapper.findWhere((w) => w.prop('data-test') === 'note') + + expect(notes).toHaveLength(0) }) it('should display the notes text field empty', async () => { const expectedNotes = 'expected notes' const expectedLab = { ...mockLab, notes: [expectedNotes] } as Lab - const wrapper = await setup(expectedLab, [Permissions.ViewLab]) + const { wrapper } = await setup(expectedLab, [Permissions.ViewLab]) const notesTextField = wrapper.find(TextFieldWithLabelFormGroup).at(1) @@ -180,8 +193,17 @@ describe('View Lab', () => { 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 { wrapper } = await setup(expectedLab, [Permissions.ViewLab, Permissions.CompleteLab]) + + const expectedError = { message: 'some message', result: 'some result feedback' } as LabError + jest.spyOn(validateUtil, 'validateLabComplete').mockReturnValue(expectedError) + + const completeButton = wrapper.find(Button).at(1) + await act(async () => { + const onClick = completeButton.prop('onClick') as any + await onClick() + }) + wrapper.update() const alert = wrapper.find(Alert) const resultTextField = wrapper.find(TextFieldWithLabelFormGroup).at(0) @@ -195,7 +217,7 @@ describe('View Lab', () => { describe('requested lab request', () => { it('should display a warning badge if the status is requested', async () => { const expectedLab = { ...mockLab, status: 'requested' } as Lab - const wrapper = await setup(expectedLab, [Permissions.ViewLab]) + const { wrapper } = await setup(expectedLab, [Permissions.ViewLab]) const labStatusDiv = wrapper.find('.lab-status') const badge = labStatusDiv.find(Badge) expect(labStatusDiv.find('h4').text().trim()).toEqual('labs.lab.status') @@ -205,7 +227,7 @@ describe('View Lab', () => { }) it('should display a update lab, complete lab, and cancel lab button if the lab is in a requested state', async () => { - const wrapper = await setup(mockLab, [ + const { wrapper } = await setup(mockLab, [ Permissions.ViewLab, Permissions.CompleteLab, Permissions.CancelLab, @@ -223,7 +245,7 @@ describe('View Lab', () => { describe('canceled lab request', () => { it('should display a danger badge if the status is canceled', async () => { const expectedLab = { ...mockLab, status: 'canceled' } as Lab - const wrapper = await setup(expectedLab, [Permissions.ViewLab]) + const { wrapper } = await setup(expectedLab, [Permissions.ViewLab]) const labStatusDiv = wrapper.find('.lab-status') const badge = labStatusDiv.find(Badge) @@ -239,7 +261,7 @@ describe('View Lab', () => { status: 'canceled', canceledOn: '2020-03-30T04:45:20.102Z', } as Lab - const wrapper = await setup(expectedLab, [Permissions.ViewLab]) + const { wrapper } = await setup(expectedLab, [Permissions.ViewLab]) const canceledOnDiv = wrapper.find('.canceled-on') expect(canceledOnDiv.find('h4').text().trim()).toEqual('labs.lab.canceledOn') @@ -252,7 +274,7 @@ describe('View Lab', () => { it('should not display update, complete, and cancel button if the lab is canceled', async () => { const expectedLab = { ...mockLab, status: 'canceled' } as Lab - const wrapper = await setup(expectedLab, [ + const { wrapper } = await setup(expectedLab, [ Permissions.ViewLab, Permissions.CompleteLab, Permissions.CancelLab, @@ -264,7 +286,7 @@ describe('View Lab', () => { it('should not display an update button if the lab is canceled', async () => { const expectedLab = { ...mockLab, status: 'canceled' } as Lab - const wrapper = await setup(expectedLab, [Permissions.ViewLab]) + const { wrapper } = await setup(expectedLab, [Permissions.ViewLab]) const updateButton = wrapper.find(Button) expect(updateButton).toHaveLength(0) @@ -273,7 +295,7 @@ describe('View Lab', () => { it('should not display notes text field if the status is canceled', async () => { const expectedLab = { ...mockLab, status: 'canceled' } as Lab - const wrapper = await setup(expectedLab, [Permissions.ViewLab]) + const { wrapper } = await setup(expectedLab, [Permissions.ViewLab]) const textsField = wrapper.find(TextFieldWithLabelFormGroup) const notesTextField = wrapper.find('notesTextField') @@ -287,7 +309,7 @@ describe('View Lab', () => { it('should display a primary badge if the status is completed', async () => { jest.resetAllMocks() const expectedLab = { ...mockLab, status: 'completed' } as Lab - const wrapper = await setup(expectedLab, [Permissions.ViewLab]) + const { wrapper } = await setup(expectedLab, [Permissions.ViewLab]) const labStatusDiv = wrapper.find('.lab-status') const badge = labStatusDiv.find(Badge) expect(labStatusDiv.find('h4').text().trim()).toEqual('labs.lab.status') @@ -302,7 +324,7 @@ describe('View Lab', () => { status: 'completed', completedOn: '2020-03-30T04:44:20.102Z', } as Lab - const wrapper = await setup(expectedLab, [Permissions.ViewLab]) + const { wrapper } = await setup(expectedLab, [Permissions.ViewLab]) const completedOnDiv = wrapper.find('.completed-on') expect(completedOnDiv.find('h4').text().trim()).toEqual('labs.lab.completedOn') @@ -315,7 +337,7 @@ describe('View Lab', () => { it('should not display update, complete, and cancel buttons if the lab is completed', async () => { const expectedLab = { ...mockLab, status: 'completed' } as Lab - const wrapper = await setup(expectedLab, [ + const { wrapper } = await setup(expectedLab, [ Permissions.ViewLab, Permissions.CompleteLab, Permissions.CancelLab, @@ -328,7 +350,7 @@ describe('View Lab', () => { it('should not display notes text field if the status is completed', async () => { const expectedLab = { ...mockLab, status: 'completed' } as Lab - const wrapper = await setup(expectedLab, [ + const { wrapper } = await setup(expectedLab, [ Permissions.ViewLab, Permissions.CompleteLab, Permissions.CancelLab, @@ -345,26 +367,26 @@ describe('View Lab', () => { describe('on update', () => { it('should update the lab with the new information', async () => { - const wrapper = await setup(mockLab, [Permissions.ViewLab]) + const { wrapper } = await setup(mockLab, [Permissions.ViewLab]) const expectedResult = 'expected result' const newNotes = 'expected notes' const resultTextField = wrapper.find(TextFieldWithLabelFormGroup).at(0) act(() => { - const onChange = resultTextField.prop('onChange') + const onChange = resultTextField.prop('onChange') as any onChange({ currentTarget: { value: expectedResult } }) }) wrapper.update() const notesTextField = wrapper.find(TextFieldWithLabelFormGroup).at(1) act(() => { - const onChange = notesTextField.prop('onChange') + const onChange = notesTextField.prop('onChange') as any onChange({ currentTarget: { value: newNotes } }) }) wrapper.update() const updateButton = wrapper.find(Button) await act(async () => { - const onClick = updateButton.prop('onClick') + const onClick = updateButton.prop('onClick') as any onClick() }) @@ -380,7 +402,7 @@ describe('View Lab', () => { describe('on complete', () => { it('should mark the status as completed and fill in the completed date with the current time', async () => { - const wrapper = await setup(mockLab, [ + const { wrapper } = await setup(mockLab, [ Permissions.ViewLab, Permissions.CompleteLab, Permissions.CancelLab, @@ -389,14 +411,14 @@ describe('View Lab', () => { const resultTextField = wrapper.find(TextFieldWithLabelFormGroup).at(0) await act(async () => { - const onChange = resultTextField.prop('onChange') + const onChange = resultTextField.prop('onChange') as any await onChange({ currentTarget: { value: expectedResult } }) }) wrapper.update() const completeButton = wrapper.find(Button).at(1) await act(async () => { - const onClick = completeButton.prop('onClick') + const onClick = completeButton.prop('onClick') as any await onClick() }) wrapper.update() @@ -416,7 +438,7 @@ describe('View Lab', () => { describe('on cancel', () => { it('should mark the status as canceled and fill in the cancelled on date with the current time', async () => { - const wrapper = await setup(mockLab, [ + const { wrapper } = await setup(mockLab, [ Permissions.ViewLab, Permissions.CompleteLab, Permissions.CancelLab, @@ -425,14 +447,14 @@ describe('View Lab', () => { const resultTextField = wrapper.find(TextFieldWithLabelFormGroup).at(0) await act(async () => { - const onChange = resultTextField.prop('onChange') + const onChange = resultTextField.prop('onChange') as any await onChange({ currentTarget: { value: expectedResult } }) }) wrapper.update() const cancelButton = wrapper.find(Button).at(2) await act(async () => { - const onClick = cancelButton.prop('onClick') + const onClick = cancelButton.prop('onClick') as any await onClick() }) wrapper.update() diff --git a/src/__tests__/labs/ViewLabs.test.tsx b/src/__tests__/labs/ViewLabs.test.tsx index 9a14691b4b..483f111d52 100644 --- a/src/__tests__/labs/ViewLabs.test.tsx +++ b/src/__tests__/labs/ViewLabs.test.tsx @@ -1,4 +1,4 @@ -import { Select, Table } from '@hospitalrun/components' +import { Select, Table, TextInput } from '@hospitalrun/components' import { act } from '@testing-library/react' import { mount, ReactWrapper } from 'enzyme' import { createMemoryHistory } from 'history' @@ -8,7 +8,6 @@ import { Router } from 'react-router-dom' import createMockStore from 'redux-mock-store' import thunk from 'redux-thunk' -import * as labsSlice from '../../labs/labs-slice' import ViewLabs from '../../labs/ViewLabs' import * as ButtonBarProvider from '../../page-header/button-toolbar/ButtonBarProvider' import * as titleUtil from '../../page-header/title/TitleContext' @@ -19,65 +18,63 @@ import { RootState } from '../../shared/store' const mockStore = createMockStore([thunk]) -let history: any -const expectedLab = { - code: 'L-1234', - id: '1234', - type: 'lab type', - patient: 'patientId', - status: 'requested', - requestedOn: new Date().toISOString(), -} as Lab - -const setup = (permissions: Permissions[] = [Permissions.ViewLabs, Permissions.RequestLab]) => { - const store = mockStore({ - user: { permissions }, - labs: { labs: [expectedLab] }, - } as any) - history = createMemoryHistory() - - jest.spyOn(titleUtil, 'useUpdateTitle').mockImplementation(() => jest.fn()) - jest.spyOn(LabRepository, 'findAll').mockResolvedValue([expectedLab]) - - const wrapper = mount( - - - - - - - , - ) - - wrapper.find(ViewLabs).props().updateTitle = jest.fn() - wrapper.update() - return { wrapper: wrapper as ReactWrapper } -} - -describe('title', () => { - it('should have called the useUpdateTitle hook', async () => { - setup() - expect(titleUtil.useUpdateTitle).toHaveBeenCalled() +describe('View Labs', () => { + let history: any + const setButtonToolBarSpy = jest.fn() + jest.spyOn(ButtonBarProvider, 'useButtonToolbarSetter').mockReturnValue(setButtonToolBarSpy) + + const setup = async (permissions: Permissions[] = []) => { + history = createMemoryHistory() + jest.spyOn(titleUtil, 'useUpdateTitle').mockImplementation(() => jest.fn()) + + const store = mockStore({ + title: '', + user: { + permissions, + }, + } as any) + + let wrapper: any + await act(async () => { + wrapper = await mount( + + + + + + + + + , + ) + }) + + wrapper.find(ViewLabs).props().updateTitle = jest.fn() + wrapper.update() + return { wrapper: wrapper as ReactWrapper } + } + + describe('title', () => { + it('should have called the useUpdateTitle hook', async () => { + await setup() + expect(titleUtil.useUpdateTitle).toHaveBeenCalled() + }) }) -}) -describe('View Labs', () => { describe('button bar', () => { + beforeEach(() => { + setButtonToolBarSpy.mockReset() + }) + it('should display button to add new lab request', async () => { - const setButtonToolBarSpy = jest.fn() - jest.spyOn(ButtonBarProvider, 'useButtonToolbarSetter').mockReturnValue(setButtonToolBarSpy) - jest.spyOn(LabRepository, 'findAll').mockResolvedValue([]) - setup() + await setup([Permissions.ViewLab, Permissions.RequestLab]) const actualButtons: React.ReactNode[] = setButtonToolBarSpy.mock.calls[0][0] expect((actualButtons[0] as any).props.children).toEqual('labs.requests.new') }) it('should not display button to add new lab request if the user does not have permissions', async () => { - const setButtonToolBarSpy = jest.fn() - jest.spyOn(ButtonBarProvider, 'useButtonToolbarSetter').mockReturnValue(setButtonToolBarSpy) - jest.spyOn(LabRepository, 'findAll').mockResolvedValue([]) - setup([]) + await setup([Permissions.ViewLabs]) const actualButtons: React.ReactNode[] = setButtonToolBarSpy.mock.calls[0][0] expect(actualButtons).toEqual([]) @@ -85,8 +82,20 @@ describe('View Labs', () => { }) describe('table', () => { - it('should render a table with data', () => { - const { wrapper } = setup() + const expectedLab = { + code: 'L-1234', + id: '1234', + type: 'lab type', + patient: 'patientId', + status: 'requested', + requestedOn: '2020-03-30T04:43:20.102Z', + } as Lab + + jest.spyOn(LabRepository, 'findAll').mockResolvedValue([expectedLab]) + + it('should render a table with data', async () => { + const { wrapper } = await setup([Permissions.ViewLabs, Permissions.RequestLab]) + const table = wrapper.find(Table) const columns = table.prop('columns') const actions = table.prop('actions') as any @@ -104,8 +113,8 @@ describe('View Labs', () => { expect(table.prop('data')).toEqual([expectedLab]) }) - it('should navigate to the lab when the view button is clicked', () => { - const { wrapper } = setup() + it('should navigate to the lab when the view button is clicked', async () => { + const { wrapper } = await setup([Permissions.ViewLabs, Permissions.RequestLab]) const tr = wrapper.find('tr').at(1) act(() => { @@ -117,55 +126,59 @@ describe('View Labs', () => { }) describe('dropdown', () => { - it('should search for labs when dropdown changes', () => { - const searchLabsSpy = jest.spyOn(labsSlice, 'searchLabs') - const { wrapper } = setup() + const searchLabsSpy = jest.spyOn(LabRepository, 'search') + beforeEach(() => { searchLabsSpy.mockClear() + }) - act(() => { + it('should search for labs when dropdown changes', async () => { + const expectedStatus = 'requested' + const { wrapper } = await setup([Permissions.ViewLabs]) + + await act(async () => { const onChange = wrapper.find(Select).prop('onChange') as any - onChange({ - target: { - value: 'requested', - }, - preventDefault: jest.fn(), - }) + await onChange([expectedStatus]) }) - wrapper.update() expect(searchLabsSpy).toHaveBeenCalledTimes(1) + expect(searchLabsSpy).toHaveBeenCalledWith( + expect.objectContaining({ status: expectedStatus }), + ) }) }) -}) - -describe('search functionality', () => { - beforeEach(() => jest.useFakeTimers()) - - afterEach(() => jest.useRealTimers()) - it('should search for labs after the search text has not changed for 500 milliseconds', async () => { - const searchLabsSpy = jest.spyOn(labsSlice, 'searchLabs') + describe('search functionality', () => { + const searchLabsSpy = jest.spyOn(LabRepository, 'search') - searchLabsSpy.mockClear() + beforeEach(() => { + searchLabsSpy.mockClear() + }) - beforeEach(async () => { - const { wrapper } = setup() + it('should search for labs after the search text has not changed for 500 milliseconds', async () => { + jest.useFakeTimers() + const { wrapper } = await setup([Permissions.ViewLabs]) - searchLabsSpy.mockClear() + const expectedSearchText = 'search text' act(() => { - const onChange = wrapper.find(Select).prop('onChange') as any + const onChange = wrapper.find(TextInput).prop('onChange') as any onChange({ target: { - value: 'requested', + value: expectedSearchText, }, preventDefault: jest.fn(), }) }) - wrapper.update() + act(() => { + jest.advanceTimersByTime(500) + }) + expect(searchLabsSpy).toHaveBeenCalledTimes(1) + expect(searchLabsSpy).toHaveBeenCalledWith( + expect.objectContaining({ text: expectedSearchText }), + ) }) }) }) diff --git a/src/__tests__/labs/hooks/useCancelLab.test.ts b/src/__tests__/labs/hooks/useCancelLab.test.ts new file mode 100644 index 0000000000..e73e9750b6 --- /dev/null +++ b/src/__tests__/labs/hooks/useCancelLab.test.ts @@ -0,0 +1,33 @@ +import { act } from '@testing-library/react-hooks' + +import useCancelLab from '../../../labs/hooks/useCancelLab' +import LabRepository from '../../../shared/db/LabRepository' +import Lab from '../../../shared/model/Lab' +import executeMutation from '../../test-utils/use-mutation.util' + +describe('Use Cancel Lab', () => { + const expectedCanceledOnDate = new Date() + const lab = { + id: 'id lab', + status: 'requested', + } as Lab + const expectedCanceledLab = { + ...lab, + status: 'canceled', + canceledOn: expectedCanceledOnDate.toISOString(), + } as Lab + + Date.now = jest.fn(() => expectedCanceledOnDate.valueOf()) + jest.spyOn(LabRepository, 'saveOrUpdate').mockResolvedValue(expectedCanceledLab) + + it('should cancel a lab', async () => { + let actualData: any + await act(async () => { + actualData = await executeMutation(() => useCancelLab(), lab) + }) + + expect(LabRepository.saveOrUpdate).toHaveBeenCalledTimes(1) + expect(LabRepository.saveOrUpdate).toHaveBeenCalledWith(lab) + expect(actualData).toEqual(expectedCanceledLab) + }) +}) diff --git a/src/__tests__/labs/hooks/useCompleteLab.test.ts b/src/__tests__/labs/hooks/useCompleteLab.test.ts new file mode 100644 index 0000000000..b275422f04 --- /dev/null +++ b/src/__tests__/labs/hooks/useCompleteLab.test.ts @@ -0,0 +1,58 @@ +import { act } from '@testing-library/react-hooks' + +import useCompleteLab from '../../../labs/hooks/useCompleteLab' +import { LabError } from '../../../labs/utils/validate-lab' +import * as validateLabUtils from '../../../labs/utils/validate-lab' +import LabRepository from '../../../shared/db/LabRepository' +import Lab from '../../../shared/model/Lab' +import executeMutation from '../../test-utils/use-mutation.util' + +describe('Use Complete lab', () => { + const expectedCompletedOnDate = new Date() + const lab = { + type: 'test', + result: 'some result', + } as Lab + const expectedCompletedLab = { + ...lab, + completedOn: expectedCompletedOnDate.toISOString(), + status: 'completed', + } as Lab + + Date.now = jest.fn(() => expectedCompletedOnDate.valueOf()) + jest.spyOn(LabRepository, 'saveOrUpdate').mockResolvedValue(expectedCompletedLab) + + beforeEach(() => { + jest.clearAllMocks() + }) + + it('should save lab as complete', async () => { + let actualData: any + await act(async () => { + actualData = await executeMutation(() => useCompleteLab(), lab) + }) + + expect(LabRepository.saveOrUpdate).toHaveBeenCalledTimes(1) + expect(LabRepository.saveOrUpdate).toHaveBeenCalledWith(expectedCompletedLab) + expect(actualData).toEqual(expectedCompletedLab) + }) + + it('should throw errors', async () => { + expect.hasAssertions() + + const expectedLabError = { + result: 'some result error message', + } as LabError + + jest.spyOn(validateLabUtils, 'validateLabComplete').mockReturnValue(expectedLabError) + + await act(async () => { + try { + await executeMutation(() => useCompleteLab(), lab) + } catch (e) { + expect(e).toEqual(expectedLabError) + expect(LabRepository.saveOrUpdate).not.toHaveBeenCalled() + } + }) + }) +}) diff --git a/src/__tests__/labs/hooks/useLab.test.ts b/src/__tests__/labs/hooks/useLab.test.ts new file mode 100644 index 0000000000..cd98fa268d --- /dev/null +++ b/src/__tests__/labs/hooks/useLab.test.ts @@ -0,0 +1,29 @@ +import { act, renderHook } from '@testing-library/react-hooks' + +import useLab from '../../../labs/hooks/useLab' +import LabRepository from '../../../shared/db/LabRepository' +import Lab from '../../../shared/model/Lab' +import waitUntilQueryIsSuccessful from '../../test-utils/wait-for-query.util' + +describe('Use lab', () => { + const expectedLabId = 'lab id' + const expectedLab = { + id: expectedLabId, + } as Lab + + jest.spyOn(LabRepository, 'find').mockResolvedValue(expectedLab) + + it('should get a lab by id', async () => { + let actualData: any + await act(async () => { + const renderHookResult = renderHook(() => useLab(expectedLabId)) + const { result } = renderHookResult + await waitUntilQueryIsSuccessful(renderHookResult) + actualData = result.current.data + }) + + expect(LabRepository.find).toHaveBeenCalledTimes(1) + expect(LabRepository.find).toHaveBeenCalledWith(expectedLabId) + expect(actualData).toEqual(expectedLab) + }) +}) diff --git a/src/__tests__/labs/hooks/useLabsSearch.test.ts b/src/__tests__/labs/hooks/useLabsSearch.test.ts new file mode 100644 index 0000000000..c0bff21528 --- /dev/null +++ b/src/__tests__/labs/hooks/useLabsSearch.test.ts @@ -0,0 +1,65 @@ +import { act, renderHook } from '@testing-library/react-hooks' + +import useLabsSearch from '../../../labs/hooks/useLabsSearch' +import LabSearchRequest from '../../../labs/model/LabSearchRequest' +import LabRepository from '../../../shared/db/LabRepository' +import Lab from '../../../shared/model/Lab' +import waitUntilQueryIsSuccessful from '../../test-utils/wait-for-query.util' + +describe('Use Labs Search', () => { + const expectedLabs = [ + { + id: 'lab id', + }, + ] as Lab[] + + const labRepositoryFindAllSpy = jest + .spyOn(LabRepository, 'findAll') + .mockResolvedValue(expectedLabs) + const labRepositorySearchSpy = jest.spyOn(LabRepository, 'search').mockResolvedValue(expectedLabs) + + beforeEach(() => { + labRepositoryFindAllSpy.mockClear() + }) + + it('should return all labs', async () => { + const expectedLabsSearchRequest = { + text: '', + status: 'all', + } as LabSearchRequest + + let actualData: any + await act(async () => { + const renderHookResult = renderHook(() => useLabsSearch(expectedLabsSearchRequest)) + const { result } = renderHookResult + await waitUntilQueryIsSuccessful(renderHookResult) + actualData = result.current.data + }) + + expect(labRepositoryFindAllSpy).toHaveBeenCalledTimes(1) + expect(labRepositorySearchSpy).not.toHaveBeenCalled() + expect(actualData).toEqual(expectedLabs) + }) + + it('should search for labs', async () => { + const expectedLabsSearchRequest = { + text: 'search text', + status: 'all', + } as LabSearchRequest + + let actualData: any + await act(async () => { + const renderHookResult = renderHook(() => useLabsSearch(expectedLabsSearchRequest)) + const { result } = renderHookResult + await waitUntilQueryIsSuccessful(renderHookResult) + actualData = result.current.data + }) + + expect(labRepositoryFindAllSpy).not.toHaveBeenCalled() + expect(labRepositorySearchSpy).toHaveBeenCalledTimes(1) + expect(labRepositorySearchSpy).toHaveBeenCalledWith( + expect.objectContaining(expectedLabsSearchRequest), + ) + expect(actualData).toEqual(expectedLabs) + }) +}) diff --git a/src/__tests__/labs/hooks/useRequestLab.test.ts b/src/__tests__/labs/hooks/useRequestLab.test.ts new file mode 100644 index 0000000000..fc83aa805c --- /dev/null +++ b/src/__tests__/labs/hooks/useRequestLab.test.ts @@ -0,0 +1,59 @@ +import { act } from '@testing-library/react-hooks' + +import useRequestLab from '../../../labs/hooks/useRequestLab' +import * as validateLabRequest from '../../../labs/utils/validate-lab' +import { LabError } from '../../../labs/utils/validate-lab' +import LabRepository from '../../../shared/db/LabRepository' +import Lab from '../../../shared/model/Lab' +import executeMutation from '../../test-utils/use-mutation.util' + +describe('Use Request lab', () => { + const expectedRequestedOnDate = new Date() + const lab = { + type: 'test', + patient: '123', + } as Lab + const expectedRequestedLab = { + ...lab, + requestedOn: expectedRequestedOnDate.toISOString(), + } as Lab + + Date.now = jest.fn(() => expectedRequestedOnDate.valueOf()) + jest.spyOn(LabRepository, 'save').mockResolvedValue(expectedRequestedLab) + + beforeEach(() => { + jest.clearAllMocks() + }) + + it('should save new request lab', async () => { + let actualData: any + await act(async () => { + actualData = await executeMutation(() => useRequestLab(), lab) + }) + + expect(LabRepository.save).toHaveBeenCalledTimes(1) + expect(LabRepository.save).toHaveBeenCalledWith(lab) + expect(actualData).toEqual(expectedRequestedLab) + }) + + it('should return errors', async () => { + expect.hasAssertions() + + const expectedError = { + message: 'error message', + patient: 'error patient', + type: 'error type', + } as LabError + + jest.spyOn(validateLabRequest, 'validateLabRequest').mockReturnValue(expectedError) + + await act(async () => { + try { + await executeMutation(() => useRequestLab(), lab) + } catch (e) { + expect(e).toEqual(expectedError) + expect(LabRepository.save).not.toHaveBeenCalled() + } + }) + }) +}) diff --git a/src/__tests__/labs/hooks/useUpdateLab.test.ts b/src/__tests__/labs/hooks/useUpdateLab.test.ts new file mode 100644 index 0000000000..6ab9840284 --- /dev/null +++ b/src/__tests__/labs/hooks/useUpdateLab.test.ts @@ -0,0 +1,27 @@ +import { act } from '@testing-library/react-hooks' + +import useUpdateLab from '../../../labs/hooks/useUpdateLab' +import LabRepository from '../../../shared/db/LabRepository' +import Lab from '../../../shared/model/Lab' +import executeMutation from '../../test-utils/use-mutation.util' + +describe('Use update lab', () => { + const expectedLab = { + type: 'some type', + notes: ['some note'], + } as Lab + + jest.spyOn(LabRepository, 'saveOrUpdate').mockResolvedValue(expectedLab) + + it('should update lab', async () => { + let actualData: any + + await act(async () => { + actualData = await executeMutation(() => useUpdateLab(), expectedLab) + }) + + expect(LabRepository.saveOrUpdate).toHaveBeenCalledTimes(1) + expect(LabRepository.saveOrUpdate).toHaveBeenCalledWith(expectedLab) + expect(actualData).toEqual(expectedLab) + }) +}) diff --git a/src/__tests__/labs/lab-slice.test.ts b/src/__tests__/labs/lab-slice.test.ts deleted file mode 100644 index 45449b3aea..0000000000 --- a/src/__tests__/labs/lab-slice.test.ts +++ /dev/null @@ -1,406 +0,0 @@ -import createMockStore from 'redux-mock-store' -import thunk from 'redux-thunk' - -import labSlice, { - requestLab, - fetchLabStart, - fetchLabSuccess, - updateLabStart, - updateLabSuccess, - requestLabStart, - requestLabSuccess, - completeLabStart, - completeLabSuccess, - cancelLabStart, - cancelLabSuccess, - fetchLab, - cancelLab, - completeLab, - completeLabError, - requestLabError, - updateLab, -} from '../../labs/lab-slice' -import LabRepository from '../../shared/db/LabRepository' -import PatientRepository from '../../shared/db/PatientRepository' -import Lab from '../../shared/model/Lab' -import Patient from '../../shared/model/Patient' -import { RootState } from '../../shared/store' - -const mockStore = createMockStore([thunk]) - -describe('lab slice', () => { - describe('reducers', () => { - describe('fetchLabStart', () => { - it('should set status to loading', async () => { - const labStore = labSlice(undefined, 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: 'patient' } as Patient - - const labStore = labSlice( - undefined, - fetchLabSuccess({ lab: expectedLab, patient: expectedPatient }), - ) - - expect(labStore.status).toEqual('completed') - expect(labStore.lab).toEqual(expectedLab) - expect(labStore.patient).toEqual(expectedPatient) - }) - }) - - describe('updateLabStart', () => { - it('should set status to loading', async () => { - const labStore = labSlice(undefined, 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(undefined, updateLabSuccess(expectedLab)) - - expect(labStore.status).toEqual('completed') - expect(labStore.lab).toEqual(expectedLab) - }) - }) - - describe('requestLabStart', () => { - it('should set status to loading', async () => { - const labStore = labSlice(undefined, 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(undefined, requestLabSuccess(expectedLab)) - - expect(labStore.status).toEqual('completed') - expect(labStore.lab).toEqual(expectedLab) - }) - }) - - describe('requestLabError', () => { - const expectedError = { message: 'some message', result: 'some result error' } - - const labStore = labSlice(undefined, requestLabError(expectedError)) - - expect(labStore.status).toEqual('error') - expect(labStore.error).toEqual(expectedError) - }) - - describe('completeLabStart', () => { - it('should set status to loading', async () => { - const labStore = labSlice(undefined, 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(undefined, completeLabSuccess(expectedLab)) - - expect(labStore.status).toEqual('completed') - expect(labStore.lab).toEqual(expectedLab) - }) - }) - - describe('completeLabError', () => { - const expectedError = { message: 'some message', result: 'some result error' } - - const labStore = labSlice(undefined, completeLabError(expectedError)) - - expect(labStore.status).toEqual('error') - expect(labStore.error).toEqual(expectedError) - }) - - describe('cancelLabStart', () => { - it('should set status to loading', async () => { - const labStore = labSlice(undefined, 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(undefined, cancelLabSuccess(expectedLab)) - - expect(labStore.status).toEqual('completed') - expect(labStore.lab).toEqual(expectedLab) - }) - }) - }) - - describe('fetch lab', () => { - let patientRepositorySpy: any - let labRepositoryFindSpy: any - - const mockLab = { - id: 'labId', - patient: 'patient', - } as Lab - - const mockPatient = { - id: 'patient', - } 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(fetchLab(mockLab.id)) - const actions = store.getActions() - - expect(actions[0]).toEqual(fetchLabStart()) - expect(labRepositoryFindSpy).toHaveBeenCalledWith(mockLab.id) - expect(patientRepositorySpy).toHaveBeenCalledWith(mockLab.patient) - expect(actions[1]).toEqual(fetchLabSuccess({ lab: mockLab, patient: mockPatient })) - }) - }) - - describe('cancel lab', () => { - const mockLab = { - id: 'labId', - patient: 'patient', - } 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(cancelLab(mockLab)) - const actions = store.getActions() - - expect(actions[0]).toEqual(cancelLabStart()) - expect(labRepositorySaveOrUpdateSpy).toHaveBeenCalledWith(expectedCanceledLab) - expect(actions[1]).toEqual(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(cancelLab(mockLab, onSuccessSpy)) - - expect(onSuccessSpy).toHaveBeenCalledWith(expectedCanceledLab) - }) - }) - - describe('complete lab', () => { - const mockLab = { - id: 'labId', - patient: 'patient', - 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(completeLab(mockLab)) - const actions = store.getActions() - - expect(actions[0]).toEqual(completeLabStart()) - expect(labRepositorySaveOrUpdateSpy).toHaveBeenCalledWith(expectedCompletedLab) - expect(actions[1]).toEqual(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(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(completeLab({ id: 'labId' } as Lab, onSuccessSpy)) - const actions = store.getActions() - - expect(actions[1]).toEqual( - completeLabError({ - result: 'labs.requests.error.resultRequiredToComplete', - message: 'labs.requests.error.unableToComplete', - }), - ) - expect(onSuccessSpy).not.toHaveBeenCalled() - }) - }) - - describe('request lab', () => { - const mockLab = { - id: 'labId', - type: 'labType', - patient: 'patient', - } 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({ - user: { - user: { - id: 'fake id', - }, - }, - } as any) - - const expectedRequestedLab = { - ...mockLab, - requestedOn: new Date(Date.now()).toISOString(), - status: 'requested', - requestedBy: store.getState().user.user.id, - } as Lab - - await store.dispatch(requestLab(mockLab)) - - const actions = store.getActions() - - expect(actions[0]).toEqual(requestLabStart()) - expect(labRepositorySaveSpy).toHaveBeenCalledWith(expectedRequestedLab) - expect(actions[1]).toEqual(requestLabSuccess(expectedRequestedLab)) - }) - - it('should execute the onSuccess callback if provided', async () => { - const store = mockStore({ - user: { - user: { - id: 'fake id', - }, - }, - } as any) - 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(requestLabStart()) - expect(actions[1]).toEqual( - 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', - patient: 'patient', - 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(updateLab(expectedUpdatedLab)) - const actions = store.getActions() - - expect(actions[0]).toEqual(updateLabStart()) - expect(labRepositorySaveOrUpdateSpy).toHaveBeenCalledWith(expectedUpdatedLab) - expect(actions[1]).toEqual(updateLabSuccess(expectedUpdatedLab)) - }) - - it('should call the onSuccess callback if successful', async () => { - const store = mockStore() - const onSuccessSpy = jest.fn() - - await store.dispatch(updateLab(expectedUpdatedLab, onSuccessSpy)) - - expect(onSuccessSpy).toHaveBeenCalledWith(expectedUpdatedLab) - }) - }) -}) diff --git a/src/__tests__/labs/labs.slice.test.ts b/src/__tests__/labs/labs.slice.test.ts deleted file mode 100644 index 5e05eb15fb..0000000000 --- a/src/__tests__/labs/labs.slice.test.ts +++ /dev/null @@ -1,140 +0,0 @@ -import { AnyAction } from 'redux' -import { mocked } from 'ts-jest/utils' - -import labs, { fetchLabsStart, fetchLabsSuccess, searchLabs } from '../../labs/labs-slice' -import LabRepository from '../../shared/db/LabRepository' -import SortRequest from '../../shared/db/SortRequest' -import Lab from '../../shared/model/Lab' - -interface SearchContainer { - text: string - status: 'requested' | 'completed' | 'canceled' | 'all' - defaultSortRequest: SortRequest -} - -const defaultSortRequest: SortRequest = { - sorts: [ - { - field: 'requestedOn', - direction: 'desc', - }, - ], -} - -const expectedSearchObject: SearchContainer = { - text: 'search string', - status: 'all', - defaultSortRequest, -} - -describe('labs slice', () => { - beforeEach(() => { - jest.resetAllMocks() - }) - - describe('labs reducer', () => { - it('should create the proper intial state with empty labs array', () => { - const labsStore = labs(undefined, {} as AnyAction) - expect(labsStore.isLoading).toBeFalsy() - expect(labsStore.labs).toHaveLength(0) - expect(labsStore.statusFilter).toEqual('all') - }) - - it('it should handle the FETCH_LABS_SUCCESS action', () => { - const expectedLabs = [{ id: '1234' }] - const labsStore = labs(undefined, { - type: fetchLabsSuccess.type, - payload: expectedLabs, - }) - - expect(labsStore.isLoading).toBeFalsy() - expect(labsStore.labs).toEqual(expectedLabs) - }) - }) - - describe('searchLabs', () => { - it('should dispatch the FETCH_LABS_START action', async () => { - const dispatch = jest.fn() - const getState = jest.fn() - - await searchLabs('search string', 'all')(dispatch, getState, null) - - expect(dispatch).toHaveBeenCalledWith({ type: fetchLabsStart.type }) - }) - - it('should call the LabRepository search method with the correct search criteria', async () => { - const dispatch = jest.fn() - const getState = jest.fn() - jest.spyOn(LabRepository, 'search') - - await searchLabs(expectedSearchObject.text, expectedSearchObject.status)( - dispatch, - getState, - null, - ) - - expect(LabRepository.search).toHaveBeenCalledWith(expectedSearchObject) - }) - - it('should call the LabRepository findAll method if there is no string text and status is set to all', async () => { - const dispatch = jest.fn() - const getState = jest.fn() - jest.spyOn(LabRepository, 'findAll') - - await searchLabs('', expectedSearchObject.status)(dispatch, getState, null) - - expect(LabRepository.findAll).toHaveBeenCalledTimes(1) - }) - - it('should dispatch the FETCH_LABS_SUCCESS action', async () => { - const dispatch = jest.fn() - const getState = jest.fn() - - const expectedLabs = [ - { - type: 'text', - }, - ] as Lab[] - - const mockedLabRepository = mocked(LabRepository, true) - mockedLabRepository.search.mockResolvedValue(expectedLabs) - - await searchLabs(expectedSearchObject.text, expectedSearchObject.status)( - dispatch, - getState, - null, - ) - - expect(dispatch).toHaveBeenLastCalledWith({ - type: fetchLabsSuccess.type, - payload: expectedLabs, - }) - }) - }) - - describe('sort Request', () => { - it('should have called findAll with sort request in searchLabs method', async () => { - const dispatch = jest.fn() - const getState = jest.fn() - jest.spyOn(LabRepository, 'findAll') - - await searchLabs('', expectedSearchObject.status)(dispatch, getState, null) - - expect(LabRepository.findAll).toHaveBeenCalledWith(expectedSearchObject.defaultSortRequest) - }) - - it('should include sorts in the search criteria', async () => { - const dispatch = jest.fn() - const getState = jest.fn() - jest.spyOn(LabRepository, 'search') - - await searchLabs(expectedSearchObject.text, expectedSearchObject.status)( - dispatch, - getState, - null, - ) - - expect(LabRepository.search).toHaveBeenCalledWith(expectedSearchObject) - }) - }) -}) diff --git a/src/__tests__/labs/requests/NewLabRequest.test.tsx b/src/__tests__/labs/requests/NewLabRequest.test.tsx index a9175944b3..f97dd730fe 100644 --- a/src/__tests__/labs/requests/NewLabRequest.test.tsx +++ b/src/__tests__/labs/requests/NewLabRequest.test.tsx @@ -9,6 +9,8 @@ import createMockStore from 'redux-mock-store' import thunk from 'redux-thunk' import NewLabRequest from '../../../labs/requests/NewLabRequest' +import * as validationUtil from '../../../labs/utils/validate-lab' +import { LabError } from '../../../labs/utils/validate-lab' import * as titleUtil from '../../../page-header/title/TitleContext' import TextFieldWithLabelFormGroup from '../../../shared/components/input/TextFieldWithLabelFormGroup' import TextInputWithLabelFormGroup from '../../../shared/components/input/TextInputWithLabelFormGroup' @@ -19,26 +21,31 @@ import Patient from '../../../shared/model/Patient' import { RootState } from '../../../shared/store' const mockStore = createMockStore([thunk]) - describe('New Lab Request', () => { - const setup = async (store = mockStore({ lab: { status: 'loading', error: {} } } as any)) => { - const history = createMemoryHistory() + let history: any + const setup = async ( + store = mockStore({ title: '', user: { user: { id: 'userId' } } } as any), + ) => { + history = createMemoryHistory() history.push(`/labs/new`) jest.spyOn(titleUtil, 'useUpdateTitle').mockImplementation(() => jest.fn()) - const wrapper: ReactWrapper = await mount( - - - - - - - , - ) + let wrapper: any + await act(async () => { + wrapper = await mount( + + + + + + + , + ) + }) wrapper.find(NewLabRequest).props().updateTitle = jest.fn() wrapper.update() - return { wrapper } + return { wrapper: wrapper as ReactWrapper } } describe('form layout', () => { @@ -98,16 +105,26 @@ describe('New Lab Request', () => { }) }) - describe('errors', async () => { + describe('errors', () => { const error = { message: 'some message', patient: 'some patient message', type: 'some type error', - } - const store = mockStore({ lab: { status: 'error', error } } as any) - const { wrapper } = await setup(store) + } as LabError + + jest.spyOn(validationUtil, 'validateLabRequest').mockReturnValue(error) it('should display errors', async () => { + const { wrapper } = await setup() + + 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) const patientTypeahead = wrapper.find(Typeahead) @@ -123,12 +140,9 @@ describe('New Lab Request', () => { }) }) - describe('on cancel', async () => { - const history = createMemoryHistory() - - const { wrapper } = await setup() - - it('should navigate back to /labs', () => { + describe('on cancel', () => { + it('should navigate back to /labs', async () => { + const { wrapper } = await setup() const cancelButton = wrapper.find(Button).at(1) act(() => { @@ -140,8 +154,7 @@ describe('New Lab Request', () => { }) }) - describe('on save', async () => { - const history = createMemoryHistory() + describe('on save', () => { let labRepositorySaveSpy: any const expectedDate = new Date() const expectedNotes = 'expected notes' @@ -157,7 +170,7 @@ describe('New Lab Request', () => { lab: { status: 'loading', error: {} }, user: { user: { id: 'fake id' } }, } as any) - const { wrapper } = await setup(store) + beforeEach(() => { jest.resetAllMocks() Date.now = jest.fn(() => expectedDate.valueOf()) @@ -169,6 +182,8 @@ describe('New Lab Request', () => { }) it('should save the lab request and navigate to "/labs/:id"', async () => { + const { wrapper } = await setup(store) + const patientTypeahead = wrapper.find(Typeahead) await act(async () => { const onChange = patientTypeahead.prop('onChange') diff --git a/src/__tests__/labs/utils/validate-lab.test.ts b/src/__tests__/labs/utils/validate-lab.test.ts new file mode 100644 index 0000000000..7d52f05c67 --- /dev/null +++ b/src/__tests__/labs/utils/validate-lab.test.ts @@ -0,0 +1,60 @@ +import { LabError, validateLabRequest, validateLabComplete } from '../../../labs/utils/validate-lab' +import Lab from '../../../shared/model/Lab' + +describe('lab validator', () => { + describe('validate request', () => { + it('should not return error when fields are filled', () => { + const lab = { + patient: 'some patient', + type: 'type test', + } as Lab + + const expectedError = {} as LabError + + const actualError = validateLabRequest(lab) + + expect(actualError).toEqual(expectedError) + }) + + it('should return error when fields are missing', () => { + const lab = {} as Lab + + const expectedError = { + patient: 'labs.requests.error.patientRequired', + type: 'labs.requests.error.typeRequired', + message: 'labs.requests.error.unableToRequest', + } as LabError + + const actualError = validateLabRequest(lab) + + expect(actualError).toEqual(expectedError) + }) + }) + + describe('validate completed', () => { + it('should not return error when result is filled', () => { + const lab = { + result: 'some result', + } as Lab + + const expectedError = {} as LabError + + const actualError = validateLabComplete(lab) + + expect(actualError).toEqual(expectedError) + }) + + it('should return error when result is missing', () => { + const lab = {} as Lab + + const expectedError = { + result: 'labs.requests.error.resultRequiredToComplete', + message: 'labs.requests.error.unableToComplete', + } as LabError + + const actualError = validateLabComplete(lab) + + expect(actualError).toEqual(expectedError) + }) + }) +}) diff --git a/src/__tests__/patients/care-goals/CareGoalTab.test.tsx b/src/__tests__/patients/care-goals/CareGoalTab.test.tsx index 57b0d42877..2ce1f2e2ae 100644 --- a/src/__tests__/patients/care-goals/CareGoalTab.test.tsx +++ b/src/__tests__/patients/care-goals/CareGoalTab.test.tsx @@ -3,7 +3,7 @@ import { createMemoryHistory } from 'history' import React from 'react' import { act } from 'react-dom/test-utils' import { Provider } from 'react-redux' -import { Router } from 'react-router-dom' +import { Router, Route } from 'react-router-dom' import createMockStore from 'redux-mock-store' import thunk from 'redux-thunk' @@ -32,7 +32,9 @@ describe('Care Goals Tab', () => { wrapper = await mount( - + + + , ) diff --git a/src/__tests__/patients/care-plans/CarePlanTab.test.tsx b/src/__tests__/patients/care-plans/CarePlanTab.test.tsx index 693541168d..62b0a30260 100644 --- a/src/__tests__/patients/care-plans/CarePlanTab.test.tsx +++ b/src/__tests__/patients/care-plans/CarePlanTab.test.tsx @@ -4,7 +4,7 @@ import { createMemoryHistory } from 'history' import React from 'react' import { act } from 'react-dom/test-utils' import { Provider } from 'react-redux' -import { Router } from 'react-router-dom' +import { Router, Route } from 'react-router-dom' import createMockStore from 'redux-mock-store' import thunk from 'redux-thunk' @@ -33,7 +33,9 @@ describe('Care Plan Tab', () => { wrapper = await mount( - + + + , ) diff --git a/src/__tests__/patients/view/ViewPatient.test.tsx b/src/__tests__/patients/view/ViewPatient.test.tsx index dcc2f9884b..b4ca4c142d 100644 --- a/src/__tests__/patients/view/ViewPatient.test.tsx +++ b/src/__tests__/patients/view/ViewPatient.test.tsx @@ -48,8 +48,11 @@ describe('ViewPatient', () => { let history: any let store: MockStore + let setButtonToolBarSpy: any const setup = async (permissions = [Permissions.ReadPatients]) => { + setButtonToolBarSpy = jest.fn() + jest.spyOn(ButtonBarProvider, 'useButtonToolbarSetter').mockReturnValue(setButtonToolBarSpy) jest.spyOn(titleUtil, 'useUpdateTitle').mockImplementation(() => jest.fn()) jest.spyOn(PatientRepository, 'find') jest.spyOn(PatientRepository, 'getLabs').mockResolvedValue([]) @@ -101,10 +104,6 @@ describe('ViewPatient', () => { }) it('should add a "Edit Patient" button to the button tool bar if has WritePatients permissions', async () => { - jest.spyOn(ButtonBarProvider, 'useButtonToolbarSetter') - const setButtonToolBarSpy = jest.fn() - mocked(ButtonBarProvider).useButtonToolbarSetter.mockReturnValue(setButtonToolBarSpy) - await setup([Permissions.WritePatients]) const actualButtons: React.ReactNode[] = setButtonToolBarSpy.mock.calls[0][0] @@ -112,10 +111,6 @@ describe('ViewPatient', () => { }) it('button toolbar empty if only has ReadPatients permission', async () => { - jest.spyOn(ButtonBarProvider, 'useButtonToolbarSetter') - const setButtonToolBarSpy = jest.fn() - mocked(ButtonBarProvider).useButtonToolbarSetter.mockReturnValue(setButtonToolBarSpy) - await setup() const actualButtons: React.ReactNode[] = setButtonToolBarSpy.mock.calls[0][0] diff --git a/src/labs/ViewLab.tsx b/src/labs/ViewLab.tsx index d253c4cb21..c9c5f05f31 100644 --- a/src/labs/ViewLab.tsx +++ b/src/labs/ViewLab.tsx @@ -1,11 +1,12 @@ import { Row, Column, Badge, Button, Alert, Toast, Callout, Label } from '@hospitalrun/components' import format from 'date-fns/format' import React, { useEffect, useState } from 'react' -import { useSelector, useDispatch } from 'react-redux' +import { useSelector } from 'react-redux' import { useParams, useHistory } from 'react-router-dom' import useAddBreadcrumbs from '../page-header/breadcrumbs/useAddBreadcrumbs' import { useUpdateTitle } from '../page-header/title/TitleContext' +import usePatient from '../patients/hooks/usePatient' import TextFieldWithLabelFormGroup from '../shared/components/input/TextFieldWithLabelFormGroup' import useTranslator from '../shared/hooks/useTranslator' import Lab from '../shared/model/Lab' @@ -13,7 +14,11 @@ import Patient from '../shared/model/Patient' import Permissions from '../shared/model/Permissions' import { RootState } from '../shared/store' import { uuid } from '../shared/util/uuid' -import { cancelLab, completeLab, updateLab, fetchLab } from './lab-slice' +import useCancelLab from './hooks/useCancelLab' +import useCompleteLab from './hooks/useCompleteLab' +import useLab from './hooks/useLab' +import useUpdateLab from './hooks/useUpdateLab' +import { LabError } from './utils/validate-lab' const getTitle = (patient: Patient | undefined, lab: Lab | undefined) => patient && lab ? `${lab.type} for ${patient.fullName}(${lab.code})` : '' @@ -22,14 +27,19 @@ const ViewLab = () => { const { id } = useParams() const { t } = useTranslator() const history = useHistory() - const dispatch = useDispatch() const { permissions } = useSelector((state: RootState) => state.user) - const { lab, patient, status, error } = useSelector((state: RootState) => state.lab) const [labToView, setLabToView] = useState() const [newNotes, setNewNotes] = useState() const [isEditable, setIsEditable] = useState(true) + const { data: lab } = useLab(id) + const { data: patient } = usePatient(lab?.patient) + const [updateLab] = useUpdateLab() + const [completeLab] = useCompleteLab() + const [cancelLab] = useCancelLab() + const [error, setError] = useState(undefined) + const updateTitle = useUpdateTitle() updateTitle(getTitle(patient, labToView)) @@ -41,12 +51,6 @@ const ViewLab = () => { ] useAddBreadcrumbs(breadcrumbs) - useEffect(() => { - if (id) { - dispatch(fetchLab(id)) - } - }, [id, dispatch]) - useEffect(() => { if (lab) { setLabToView({ ...lab }) @@ -66,45 +70,46 @@ const ViewLab = () => { } const onUpdate = async () => { - const onSuccess = (update: Lab) => { - history.push(`/labs/${update.id}`) - Toast( - 'success', - t('states.success'), - `${t('labs.successfullyUpdated')} ${update.type} for ${patient?.fullName}`, - ) - } if (labToView) { const newLab = labToView as Lab + if (newNotes) { newLab.notes = newLab.notes ? [...newLab.notes, newNotes] : [newNotes] setNewNotes('') } - dispatch(updateLab(newLab, onSuccess)) - } - } - const onComplete = async () => { - const onSuccess = (complete: Lab) => { - history.push(`/labs/${complete.id}`) + + const updatedLab = await updateLab(newLab) + history.push(`/labs/${updatedLab?.id}`) Toast( 'success', t('states.success'), - `${t('labs.successfullyCompleted')} ${complete.type} for ${patient?.fullName} `, + `${t('labs.successfullyUpdated')} ${updatedLab?.type} for ${patient?.fullName}`, ) } + setError(undefined) + } - if (labToView) { - dispatch(completeLab(labToView, onSuccess)) + const onComplete = async () => { + try { + if (labToView) { + const completedLab = await completeLab(labToView) + history.push(`/labs/${completedLab?.id}`) + Toast( + 'success', + t('states.success'), + `${t('labs.successfullyCompleted')} ${completedLab?.type} for ${patient?.fullName} `, + ) + } + setError(undefined) + } catch (e) { + setError(e) } } const onCancel = async () => { - const onSuccess = () => { - history.push('/labs') - } - if (labToView) { - dispatch(cancelLab(labToView, onSuccess)) + cancelLab(labToView) + history.push('/labs') } } @@ -188,7 +193,7 @@ const ViewLab = () => { return ( <> - {status === 'error' && ( + {error && ( )} @@ -227,8 +232,8 @@ const ViewLab = () => { label={t('labs.lab.result')} value={labToView.result} isEditable={isEditable} - isInvalid={!!error.result} - feedback={t(error.result as string)} + isInvalid={!!error?.result} + feedback={t(error?.result as string)} onChange={onResultChange} />