From ceb96a4af48d4108babb7ff4f5d3e75bf74ec1fb Mon Sep 17 00:00:00 2001 From: Kumiko Date: Wed, 17 Jun 2020 16:57:54 -0700 Subject: [PATCH] feat(patient): multiple contact info (#2113) Co-authored-by: Jack Meyer --- src/__tests__/patients/ContactInfo.test.tsx | 231 +++++++++ .../patients/GeneralInformation.test.tsx | 437 ++++++++++-------- .../patients/edit/EditPatient.test.tsx | 11 +- .../patients/new/NewPatient.test.tsx | 10 +- src/__tests__/patients/patient-slice.test.ts | 19 +- .../input/SelectWithLableFormGroup.tsx | 4 +- .../input/TextFieldWithLabelFormGroup.tsx | 4 +- .../input/TextInputWithLabelFormGroup.tsx | 4 +- .../enUs/translations/actions/index.ts | 1 + .../enUs/translations/patient/index.ts | 10 + src/model/ContactInformation.ts | 13 +- src/patients/ContactInfo.tsx | 146 ++++++ src/patients/ContactInfoTypes.ts | 9 + src/patients/GeneralInformation.tsx | 197 ++++---- src/patients/edit/EditPatient.tsx | 29 +- src/patients/new/NewPatient.tsx | 26 +- src/patients/patient-slice.ts | 47 +- src/patients/util/set-patient-helper.ts | 39 ++ 18 files changed, 872 insertions(+), 365 deletions(-) create mode 100644 src/__tests__/patients/ContactInfo.test.tsx create mode 100644 src/patients/ContactInfo.tsx create mode 100644 src/patients/ContactInfoTypes.ts create mode 100644 src/patients/util/set-patient-helper.ts diff --git a/src/__tests__/patients/ContactInfo.test.tsx b/src/__tests__/patients/ContactInfo.test.tsx new file mode 100644 index 0000000000..207d946d72 --- /dev/null +++ b/src/__tests__/patients/ContactInfo.test.tsx @@ -0,0 +1,231 @@ +import '../../__mocks__/matchMediaMock' +import { Column, Spinner } from '@hospitalrun/components' +import { mount } from 'enzyme' +import { createMemoryHistory } from 'history' +import React from 'react' +import { act } from 'react-dom/test-utils' +import { Router } from 'react-router-dom' + +import TextInputWithLabelFormGroup from '../../components/input/TextInputWithLabelFormGroup' +import { ContactInfoPiece } from '../../model/ContactInformation' +import ContactInfo from '../../patients/ContactInfo' +import * as uuid from '../../util/uuid' + +describe('Contact Info in its Editable mode', () => { + const data = [ + { id: '123', value: '123456', type: 'home' }, + { id: '456', value: '789012', type: undefined }, + ] + const dataForNoAdd = [ + { id: '123', value: '123456', type: 'home' }, + { id: '456', value: ' ', type: undefined }, + ] + const errors = ['this is an error', ''] + const label = 'this is a label' + const name = 'this is a name' + let onChange: jest.Mock + + const setup = (_data?: ContactInfoPiece[], _errors?: string[]) => { + const history = createMemoryHistory() + history.push('/patients/new') + onChange = jest.fn() + + const wrapper = mount( + + + , + ) + return wrapper + } + + it('should show a spinner if no data is present', () => { + const wrapper = setup() + const spinnerWrapper = wrapper.find(Spinner) + + expect(spinnerWrapper).toHaveLength(1) + }) + + it('should call onChange if no data is provided', () => { + const newId = 'newId' + jest.spyOn(uuid, 'uuid').mockReturnValue(newId) + setup() + + const expectedNewData = [{ id: newId, value: '' }] + expect(onChange).toHaveBeenCalledTimes(1) + expect(onChange).toHaveBeenCalledWith(expectedNewData) + }) + + it('should render the labels if data is provided', () => { + const wrapper = setup(data) + const headerWrapper = wrapper.find('.header') + const columnWrappers = headerWrapper.find(Column) + const expectedTypeLabel = 'patient.contactInfoType.label' + + expect(columnWrappers.at(0).text()).toEqual(`${expectedTypeLabel} & ${label}`) + expect(columnWrappers.at(1).text()).toEqual(label) + }) + + it('should display the entries if data is provided', () => { + const wrapper = setup(data) + for (let i = 0; i < wrapper.length; i += 1) { + const inputWrapper = wrapper.findWhere((w: any) => w.prop('name') === `${name}${i}`) + + expect(inputWrapper.prop('value')).toEqual(data[i].value) + } + }) + + it('should display the error if error is provided', () => { + const wrapper = setup(data, errors) + const feedbackWrappers = wrapper.find('.invalid-feedback') + + expect(feedbackWrappers).toHaveLength(errors.length * 2) + + for (let i = 0; i < feedbackWrappers.length; i += 1) { + if (i % 2 === 1) { + const j = (i - 1) / 2 + expect(feedbackWrappers.at(i).text()).toEqual(errors[j]) + } + } + }) + + it('should display the add button', () => { + const wrapper = setup(data) + const buttonWrapper = wrapper.find('button') + + expect(buttonWrapper.text().trim()).toEqual('actions.add') + }) + + it('should call the onChange callback if select is changed', () => { + const wrapper = setup(data) + const select = wrapper.findWhere((w: any) => w.prop('name') === `${name}Type0`).find('select') + select.getDOMNode().value = 'mobile' + select.simulate('change') + + const expectedNewData = [ + { id: '123', value: '123456', type: 'mobile' }, + { id: '456', value: '789012', type: undefined }, + ] + expect(onChange).toHaveBeenCalledTimes(1) + expect(onChange).toHaveBeenCalledWith(expectedNewData) + }) + + it('should call the onChange callback if input is changed', () => { + const wrapper = setup(data) + const input = wrapper.findWhere((w: any) => w.prop('name') === `${name}0`).find('input') + input.getDOMNode().value = '777777' + input.simulate('change') + + const expectedNewData = [ + { id: '123', value: '777777', type: 'home' }, + { id: '456', value: '789012', type: undefined }, + ] + expect(onChange).toHaveBeenCalledTimes(1) + expect(onChange).toHaveBeenCalledWith(expectedNewData) + }) + + it('should call the onChange callback if an add button is clicked with valid entries', () => { + const wrapper = setup(data) + const buttonWrapper = wrapper.find('button') + const onClick = buttonWrapper.prop('onClick') as any + const newId = 'newId' + jest.spyOn(uuid, 'uuid').mockReturnValue(newId) + + act(() => { + onClick() + }) + + const expectedNewData = [...data, { id: newId, value: '' }] + + expect(onChange).toHaveBeenCalledTimes(1) + expect(onChange).toHaveBeenCalledWith(expectedNewData) + }) + + it('should call the onChange callback if an add button is clicked with an empty entry', () => { + const wrapper = setup(dataForNoAdd) + const buttonWrapper = wrapper.find('button') + const onClick = buttonWrapper.prop('onClick') as any + const newId = 'newId' + jest.spyOn(uuid, 'uuid').mockReturnValue(newId) + + act(() => { + onClick() + }) + + const expectedNewData = [ + { id: '123', value: '123456', type: 'home' }, + { id: newId, value: '' }, + ] + + expect(onChange).toHaveBeenCalledTimes(1) + expect(onChange).toHaveBeenCalledWith(expectedNewData) + }) +}) + +describe('Contact Info in its non-Editable mode', () => { + const data = [ + { id: '123', value: '123456', type: 'home' }, + { id: '456', value: '789012', type: undefined }, + ] + const label = 'this is a label' + const name = 'this is a name' + + const setup = (_data?: ContactInfoPiece[]) => { + const history = createMemoryHistory() + history.push('/patients/new') + + const wrapper = mount( + + + , + ) + return wrapper + } + + it('should render an empty element if no data is present', () => { + const wrapper = setup() + const contactInfoWrapper = wrapper.find(ContactInfo) + + expect(contactInfoWrapper.find('div')).toHaveLength(1) + expect(contactInfoWrapper.containsMatchingElement(
)).toEqual(true) + }) + + it('should render the labels if data is provided', () => { + const wrapper = setup(data) + const headerWrapper = wrapper.find('.header') + const columnWrappers = headerWrapper.find(Column) + const expectedTypeLabel = 'patient.contactInfoType.label' + + expect(columnWrappers.at(0).text()).toEqual(`${expectedTypeLabel} & ${label}`) + expect(columnWrappers.at(1).text()).toEqual(label) + }) + + it('should display the entries if data is provided', () => { + const wrapper = setup(data) + for (let i = 0; i < wrapper.length; i += 1) { + const inputWrapper = wrapper.findWhere((w: any) => w.prop('name') === `${name}${i}`) + + expect(inputWrapper.prop('value')).toEqual(data[i].value) + } + }) + + it('should show inputs that are not editable', () => { + const wrapper = setup(data) + const inputWrappers = wrapper.find(TextInputWithLabelFormGroup) + for (let i = 0; i < inputWrappers.length; i += 1) { + expect(inputWrappers.at(i).prop('isEditable')).toBeFalsy() + } + }) +}) diff --git a/src/__tests__/patients/GeneralInformation.test.tsx b/src/__tests__/patients/GeneralInformation.test.tsx index 353334e293..dde42ec3b5 100644 --- a/src/__tests__/patients/GeneralInformation.test.tsx +++ b/src/__tests__/patients/GeneralInformation.test.tsx @@ -4,7 +4,7 @@ import { Alert } from '@hospitalrun/components' import { act } from '@testing-library/react' import { startOfDay, subYears } from 'date-fns' import { mount, ReactWrapper } from 'enzyme' -import { createMemoryHistory } from 'history' +import { createMemoryHistory, MemoryHistory } from 'history' import React from 'react' import { Router } from 'react-router-dom' @@ -17,32 +17,50 @@ describe('Error handling', () => { message: 'some message', givenName: 'given name message', dateOfBirth: 'date of birth message', - phoneNumber: 'phone number message', - email: 'email message', + phoneNumbers: ['phone number message'], + emails: ['email message'], } - const wrapper = mount() + const wrapper = mount( + , + ) wrapper.update() const errorMessage = wrapper.find(Alert) const givenNameInput = wrapper.findWhere((w: any) => w.prop('name') === 'givenName') const dateOfBirthInput = wrapper.findWhere((w: any) => w.prop('name') === 'dateOfBirth') - const emailInput = wrapper.findWhere((w: any) => w.prop('name') === 'email') - const phoneNumberInput = wrapper.findWhere((w: any) => w.prop('name') === 'phoneNumber') + const phoneNumberInput = wrapper.findWhere((w: any) => w.prop('name') === 'phoneNumber0') + const emailInput = wrapper.findWhere((w: any) => w.prop('name') === 'email0') + expect(errorMessage).toBeTruthy() expect(errorMessage.prop('message')).toMatch(error.message) + expect(givenNameInput.prop('isInvalid')).toBeTruthy() expect(givenNameInput.prop('feedback')).toEqual(error.givenName) + expect(dateOfBirthInput.prop('isInvalid')).toBeTruthy() expect(dateOfBirthInput.prop('feedback')).toEqual(error.dateOfBirth) - expect(emailInput.prop('feedback')).toEqual(error.email) - expect(emailInput.prop('isInvalid')).toBeTruthy() - expect(phoneNumberInput.prop('feedback')).toEqual(error.phoneNumber) + expect(phoneNumberInput.prop('isInvalid')).toBeTruthy() + expect(phoneNumberInput.prop('feedback')).toEqual(error.phoneNumbers[0]) + + expect(emailInput.prop('isInvalid')).toBeTruthy() + expect(emailInput.prop('feedback')).toEqual(error.emails[0]) }) }) describe('General Information, without isEditable', () => { + let wrapper: ReactWrapper + let history = createMemoryHistory() const patient = { id: '123', prefix: 'prefix', @@ -51,19 +69,25 @@ describe('General Information, without isEditable', () => { suffix: 'suffix', sex: 'male', type: 'charity', + dateOfBirth: startOfDay(subYears(new Date(), 30)).toISOString(), + isApproximateDateOfBirth: false, occupation: 'occupation', preferredLanguage: 'preferredLanguage', - phoneNumber: 'phoneNumber', - email: 'email@email.com', - address: 'address', + phoneNumbers: [ + { value: '123456', type: undefined }, + { value: '789012', type: undefined }, + ], + emails: [ + { value: 'abc@email.com', type: undefined }, + { value: 'xyz@email.com', type: undefined }, + ], + addresses: [ + { value: 'address A', type: undefined }, + { value: 'address B', type: undefined }, + ], code: 'P00001', - dateOfBirth: startOfDay(subYears(new Date(), 30)).toISOString(), - isApproximateDateOfBirth: false, } as Patient - let wrapper: ReactWrapper - let history = createMemoryHistory() - beforeEach(() => { Date.now = jest.fn().mockReturnValue(new Date().valueOf()) jest.restoreAllMocks() @@ -139,6 +163,23 @@ describe('General Information, without isEditable', () => { expect(dateOfBirthInput.prop('isEditable')).toBeFalsy() }) + it('should render the approximate age if patient.isApproximateDateOfBirth is true', async () => { + patient.isApproximateDateOfBirth = true + await act(async () => { + wrapper = await mount( + + ) + , + ) + }) + + const approximateAgeInput = wrapper.findWhere((w: any) => w.prop('name') === 'approximateAge') + + expect(approximateAgeInput.prop('value')).toEqual('30') + expect(approximateAgeInput.prop('label')).toEqual('patient.approximateAge') + expect(approximateAgeInput.prop('isEditable')).toBeFalsy() + }) + it('should render the occupation of the patient', () => { const occupationInput = wrapper.findWhere((w: any) => w.prop('name') === 'occupation') expect(occupationInput.prop('value')).toEqual(patient.occupation) @@ -155,48 +196,35 @@ describe('General Information, without isEditable', () => { expect(preferredLanguageInput.prop('isEditable')).toBeFalsy() }) - it('should render the phone number of the patient', () => { - const phoneNumberInput = wrapper.findWhere((w: any) => w.prop('name') === 'phoneNumber') - expect(phoneNumberInput.prop('value')).toEqual(patient.phoneNumber) - expect(phoneNumberInput.prop('label')).toEqual('patient.phoneNumber') - expect(phoneNumberInput.prop('isEditable')).toBeFalsy() - }) - - it('should render the email of the patient', () => { - const emailInput = wrapper.findWhere((w: any) => w.prop('name') === 'email') - expect(emailInput.prop('value')).toEqual(patient.email) - expect(emailInput.prop('label')).toEqual('patient.email') - expect(emailInput.prop('isEditable')).toBeFalsy() + it('should render the phone numbers of the patient', () => { + patient.phoneNumbers.forEach((phoneNumber, i) => { + const phoneNumberInput = wrapper.findWhere((w: any) => w.prop('name') === `phoneNumber${i}`) + expect(phoneNumberInput.prop('value')).toEqual(phoneNumber.value) + expect(phoneNumberInput.prop('isEditable')).toBeFalsy() + }) }) - it('should render the address of the patient', () => { - const addressInput = wrapper.findWhere((w: any) => w.prop('name') === 'address') - expect(addressInput.prop('value')).toEqual(patient.address) - expect(addressInput.prop('label')).toEqual('patient.address') - expect(addressInput.prop('isEditable')).toBeFalsy() + it('should render the emails of the patient', () => { + patient.emails.forEach((email, i) => { + const emailInput = wrapper.findWhere((w: any) => w.prop('name') === `email${i}`) + expect(emailInput.prop('value')).toEqual(email.value) + expect(emailInput.prop('isEditable')).toBeFalsy() + }) }) - it('should render the approximate age if patient.isApproximateDateOfBirth is true', async () => { - patient.isApproximateDateOfBirth = true - await act(async () => { - wrapper = await mount( - - ) - , - ) + it('should render the addresses of the patient', () => { + patient.addresses.forEach((address, i) => { + const addressInput = wrapper.findWhere((w: any) => w.prop('name') === `address${i}`) + expect(addressInput.prop('value')).toEqual(address.value) + expect(addressInput.prop('isEditable')).toBeFalsy() }) - - wrapper.update() - - const ageInput = wrapper.findWhere((w: any) => w.prop('name') === 'approximateAge') - - expect(ageInput.prop('value')).toEqual('30') - expect(ageInput.prop('label')).toEqual('patient.approximateAge') - expect(ageInput.prop('isEditable')).toBeFalsy() }) }) describe('General Information, isEditable', () => { + let wrapper: ReactWrapper + let history: MemoryHistory + let onFieldChange: jest.Mock const patient = { id: '123', prefix: 'prefix', @@ -205,28 +233,33 @@ describe('General Information, isEditable', () => { suffix: 'suffix', sex: 'male', type: 'charity', + dateOfBirth: startOfDay(subYears(new Date(), 30)).toISOString(), + isApproximateDateOfBirth: false, occupation: 'occupation', preferredLanguage: 'preferredLanguage', - phoneNumber: 'phoneNumber', - email: 'email@email.com', - address: 'address', + phoneNumbers: [ + { value: '123456', type: undefined }, + { value: '789012', type: undefined }, + ], + emails: [ + { value: 'abc@email.com', type: undefined }, + { value: 'xyz@email.com', type: undefined }, + ], + addresses: [ + { value: 'address A', type: undefined }, + { value: 'address B', type: undefined }, + ], code: 'P00001', - dateOfBirth: startOfDay(subYears(new Date(), 30)).toISOString(), - isApproximateDateOfBirth: false, } as Patient - let wrapper: ReactWrapper - let history = createMemoryHistory() - - const onFieldChange = jest.fn() - beforeEach(() => { jest.restoreAllMocks() Date.now = jest.fn().mockReturnValue(new Date().valueOf()) history = createMemoryHistory() + onFieldChange = jest.fn() wrapper = mount( - ) + ) , ) }) @@ -235,87 +268,92 @@ describe('General Information, isEditable', () => { const expectedGivenName = 'expectedGivenName' const expectedFamilyName = 'expectedFamilyName' const expectedSuffix = 'expectedSuffix' - const expectedSex = 'expectedSex' - const expectedType = 'expectedType' + const expectedSex = 'unknown' + const expectedType = 'private' + const expectedDateOfBirth = '1937-06-14T05:00:00.000Z' const expectedOccupation = 'expectedOccupation' const expectedPreferredLanguage = 'expectedPreferredLanguage' - const expectedPhoneNumber = 'expectedPhoneNumber' - const expectedEmail = 'expectedEmail' - const expectedAddress = 'expectedAddress' - const expectedDateOfBirth = '1937-06-14T05:00:00.000Z' + const expectedPhoneNumbers = [ + { value: '111111', type: undefined }, + { value: '222222', type: undefined }, + ] + const expectedEmails = [ + { value: 'def@email.com', type: undefined }, + { value: 'uvw@email.com', type: undefined }, + ] + const expectedAddresses = [ + { value: 'address C', type: undefined }, + { value: 'address D', type: undefined }, + ] it('should render the prefix', () => { const prefixInput = wrapper.findWhere((w: any) => w.prop('name') === 'prefix') - const generalInformation = wrapper.find(GeneralInformation) + expect(prefixInput.prop('value')).toEqual(patient.prefix) expect(prefixInput.prop('label')).toEqual('patient.prefix') expect(prefixInput.prop('isEditable')).toBeTruthy() - act(() => { - prefixInput.prop('onChange')({ target: { value: expectedPrefix } }) - }) + const input = prefixInput.find('input') + input.getDOMNode().value = expectedPrefix + input.simulate('change') - expect(generalInformation.prop('onFieldChange')).toHaveBeenCalledWith('prefix', expectedPrefix) + expect(onFieldChange).toHaveBeenCalledTimes(1) + expect(onFieldChange).toHaveBeenCalledWith({ ...patient, prefix: expectedPrefix }) }) it('should render the given name', () => { const givenNameInput = wrapper.findWhere((w: any) => w.prop('name') === 'givenName') - const generalInformation = wrapper.find(GeneralInformation) + expect(givenNameInput.prop('value')).toEqual(patient.givenName) expect(givenNameInput.prop('label')).toEqual('patient.givenName') expect(givenNameInput.prop('isEditable')).toBeTruthy() - act(() => { - givenNameInput.prop('onChange')({ target: { value: expectedGivenName } }) - }) + const input = givenNameInput.find('input') + input.getDOMNode().value = expectedGivenName + input.simulate('change') - expect(generalInformation.prop('onFieldChange')).toHaveBeenCalledWith( - 'givenName', - expectedGivenName, - ) + expect(onFieldChange).toHaveBeenCalledTimes(1) + expect(onFieldChange).toHaveBeenCalledWith({ ...patient, givenName: expectedGivenName }) }) it('should render the family name', () => { const familyNameInput = wrapper.findWhere((w: any) => w.prop('name') === 'familyName') - const generalInformation = wrapper.find(GeneralInformation) expect(familyNameInput.prop('value')).toEqual(patient.familyName) expect(familyNameInput.prop('label')).toEqual('patient.familyName') expect(familyNameInput.prop('isEditable')).toBeTruthy() - act(() => { - familyNameInput.prop('onChange')({ target: { value: expectedFamilyName } }) - }) + const input = familyNameInput.find('input') + input.getDOMNode().value = expectedFamilyName + input.simulate('change') - expect(generalInformation.prop('onFieldChange')).toHaveBeenCalledWith( - 'familyName', - expectedFamilyName, - ) + expect(onFieldChange).toHaveBeenCalledTimes(1) + expect(onFieldChange).toHaveBeenCalledWith({ ...patient, familyName: expectedFamilyName }) }) it('should render the suffix', () => { const suffixInput = wrapper.findWhere((w: any) => w.prop('name') === 'suffix') - const generalInformation = wrapper.find(GeneralInformation) expect(suffixInput.prop('value')).toEqual(patient.suffix) expect(suffixInput.prop('label')).toEqual('patient.suffix') expect(suffixInput.prop('isEditable')).toBeTruthy() - act(() => { - suffixInput.prop('onChange')({ target: { value: expectedSuffix } }) - }) + const input = suffixInput.find('input') + input.getDOMNode().value = expectedSuffix + input.simulate('change') - expect(generalInformation.prop('onFieldChange')).toHaveBeenCalledWith('suffix', expectedSuffix) + expect(onFieldChange).toHaveBeenCalledTimes(1) + expect(onFieldChange).toHaveBeenCalledWith({ ...patient, suffix: expectedSuffix }) }) it('should render the sex select', () => { const sexSelect = wrapper.findWhere((w: any) => w.prop('name') === 'sex') - const generalInformation = wrapper.find(GeneralInformation) expect(sexSelect.prop('value')).toEqual(patient.sex) expect(sexSelect.prop('label')).toEqual('patient.sex') expect(sexSelect.prop('isEditable')).toBeTruthy() expect(sexSelect.prop('options')).toHaveLength(4) + expect(sexSelect.prop('options')[0].label).toEqual('sex.male') expect(sexSelect.prop('options')[0].value).toEqual('male') expect(sexSelect.prop('options')[1].label).toEqual('sex.female') @@ -325,36 +363,37 @@ describe('General Information, isEditable', () => { expect(sexSelect.prop('options')[3].label).toEqual('sex.unknown') expect(sexSelect.prop('options')[3].value).toEqual('unknown') - act(() => { - sexSelect.prop('onChange')({ target: { value: expectedSex } }) - }) + const select = sexSelect.find('select') + select.getDOMNode().value = expectedSex + select.simulate('change') - expect(generalInformation.prop('onFieldChange')).toHaveBeenCalledWith('sex', expectedSex) + expect(onFieldChange).toHaveBeenCalledTimes(1) + expect(onFieldChange).toHaveBeenCalledWith({ ...patient, sex: expectedSex }) }) it('should render the patient type select', () => { const typeSelect = wrapper.findWhere((w: any) => w.prop('name') === 'type') - const generalInformation = wrapper.find(GeneralInformation) expect(typeSelect.prop('value')).toEqual(patient.type) expect(typeSelect.prop('label')).toEqual('patient.type') expect(typeSelect.prop('isEditable')).toBeTruthy() + expect(typeSelect.prop('options')).toHaveLength(2) expect(typeSelect.prop('options')[0].label).toEqual('patient.types.charity') expect(typeSelect.prop('options')[0].value).toEqual('charity') expect(typeSelect.prop('options')[1].label).toEqual('patient.types.private') expect(typeSelect.prop('options')[1].value).toEqual('private') - act(() => { - typeSelect.prop('onChange')({ target: { value: expectedType } }) - }) + const select = typeSelect.find('select') + select.getDOMNode().value = expectedType + select.simulate('change') - expect(generalInformation.prop('onFieldChange')).toHaveBeenCalledWith('type', expectedType) + expect(onFieldChange).toHaveBeenCalledTimes(1) + expect(onFieldChange).toHaveBeenCalledWith({ ...patient, type: expectedType }) }) it('should render the date of the birth of the patient', () => { const dateOfBirthInput = wrapper.findWhere((w: any) => w.prop('name') === 'dateOfBirth') - const generalInformation = wrapper.find(GeneralInformation) expect(dateOfBirthInput.prop('value')).toEqual(new Date(patient.dateOfBirth)) expect(dateOfBirthInput.prop('label')).toEqual('patient.dateOfBirth') @@ -365,126 +404,156 @@ describe('General Information, isEditable', () => { dateOfBirthInput.prop('onChange')(new Date(expectedDateOfBirth)) }) - expect(generalInformation.prop('onFieldChange')).toHaveBeenCalledWith( - 'dateOfBirth', - expectedDateOfBirth, - ) + expect(onFieldChange).toHaveBeenCalledTimes(1) + expect(onFieldChange).toHaveBeenCalledWith({ ...patient, dateOfBirth: expectedDateOfBirth }) + }) + + it('should render the approximate age if patient.isApproximateDateOfBirth is true', async () => { + patient.isApproximateDateOfBirth = true + await act(async () => { + wrapper = await mount( + + ) + , + ) + }) + + const approximateAgeInput = wrapper.findWhere((w: any) => w.prop('name') === 'approximateAge') + + expect(approximateAgeInput.prop('value')).toEqual('30') + expect(approximateAgeInput.prop('label')).toEqual('patient.approximateAge') + expect(approximateAgeInput.prop('isEditable')).toBeTruthy() + + const input = approximateAgeInput.find('input') + input.getDOMNode().value = '20' + input.simulate('change') + + expect(onFieldChange).toHaveBeenCalledTimes(1) + expect(onFieldChange).toHaveBeenCalledWith({ + ...patient, + dateOfBirth: startOfDay(subYears(new Date(Date.now()), 20)).toISOString(), + }) }) it('should render the occupation of the patient', () => { const occupationInput = wrapper.findWhere((w: any) => w.prop('name') === 'occupation') - const generalInformation = wrapper.find(GeneralInformation) expect(occupationInput.prop('value')).toEqual(patient.occupation) expect(occupationInput.prop('label')).toEqual('patient.occupation') expect(occupationInput.prop('isEditable')).toBeTruthy() - act(() => { - occupationInput.prop('onChange')({ target: { value: expectedOccupation } }) - }) + const input = occupationInput.find('input') + input.getDOMNode().value = expectedOccupation + input.simulate('change') - expect(generalInformation.prop('onFieldChange')).toHaveBeenCalledWith( - 'occupation', - expectedOccupation, - ) + expect(onFieldChange).toHaveBeenCalledTimes(1) + expect(onFieldChange).toHaveBeenCalledWith({ ...patient, occupation: expectedOccupation }) }) it('should render the preferred language of the patient', () => { const preferredLanguageInput = wrapper.findWhere( (w: any) => w.prop('name') === 'preferredLanguage', ) - const generalInformation = wrapper.find(GeneralInformation) expect(preferredLanguageInput.prop('value')).toEqual(patient.preferredLanguage) expect(preferredLanguageInput.prop('label')).toEqual('patient.preferredLanguage') expect(preferredLanguageInput.prop('isEditable')).toBeTruthy() - act(() => { - preferredLanguageInput.prop('onChange')({ target: { value: expectedPreferredLanguage } }) - }) + const input = preferredLanguageInput.find('input') + input.getDOMNode().value = expectedPreferredLanguage + input.simulate('change') - expect(generalInformation.prop('onFieldChange')).toHaveBeenCalledWith( - 'preferredLanguage', - expectedPreferredLanguage, - ) - }) - - it('should render the phone number of the patient', () => { - const phoneNumberInput = wrapper.findWhere((w: any) => w.prop('name') === 'phoneNumber') - const generalInformation = wrapper.find(GeneralInformation) - - expect(phoneNumberInput.prop('value')).toEqual(patient.phoneNumber) - expect(phoneNumberInput.prop('label')).toEqual('patient.phoneNumber') - expect(phoneNumberInput.prop('isEditable')).toBeTruthy() - - act(() => { - phoneNumberInput.prop('onChange')({ target: { value: expectedPhoneNumber } }) + expect(onFieldChange).toHaveBeenCalledTimes(1) + expect(onFieldChange).toHaveBeenCalledWith({ + ...patient, + preferredLanguage: expectedPreferredLanguage, }) - - expect(generalInformation.prop('onFieldChange')).toHaveBeenCalledWith( - 'phoneNumber', - expectedPhoneNumber, - ) }) - it('should render the email of the patient', () => { - const emailInput = wrapper.findWhere((w: any) => w.prop('name') === 'email') - const generalInformation = wrapper.find(GeneralInformation) + it('should render the phone numbers of the patient', () => { + patient.phoneNumbers.forEach((phoneNumber, i) => { + const phoneNumberInput = wrapper.findWhere((w: any) => w.prop('name') === `phoneNumber${i}`) + expect(phoneNumberInput.prop('value')).toEqual(phoneNumber.value) + expect(phoneNumberInput.prop('isEditable')).toBeTruthy() - expect(emailInput.prop('value')).toEqual(patient.email) - expect(emailInput.prop('label')).toEqual('patient.email') - expect(emailInput.prop('isEditable')).toBeTruthy() + const input = phoneNumberInput.find('input') + input.getDOMNode().value = expectedPhoneNumbers[i].value + input.simulate('change') + }) - act(() => { - emailInput.prop('onChange')({ target: { value: expectedEmail } }) + const calledWith = [] as any + patient.phoneNumbers.forEach((_, i) => { + const newPhoneNumbers = [] as any + patient.phoneNumbers.forEach((__, j) => { + if (j <= i) { + newPhoneNumbers.push(expectedPhoneNumbers[j]) + } else { + newPhoneNumbers.push(patient.phoneNumbers[j]) + } + }) + calledWith.push({ ...patient, phoneNumbers: newPhoneNumbers }) }) - expect(generalInformation.prop('onFieldChange')).toHaveBeenCalledWith('email', expectedEmail) + expect(onFieldChange).toHaveBeenCalledTimes(calledWith.length) + expect(onFieldChange).toHaveBeenNthCalledWith(1, calledWith[0]) + // expect(onFieldChange).toHaveBeenNthCalledWith(2, calledWith[1]) }) - it('should render the address of the patient', () => { - const addressInput = wrapper.findWhere((w: any) => w.prop('name') === 'address') - const generalInformation = wrapper.find(GeneralInformation) + it('should render the emails of the patient', () => { + patient.emails.forEach((email, i) => { + const emailInput = wrapper.findWhere((w: any) => w.prop('name') === `email${i}`) + expect(emailInput.prop('value')).toEqual(email.value) + expect(emailInput.prop('isEditable')).toBeTruthy() - expect(addressInput.prop('value')).toEqual(patient.address) - expect(addressInput.prop('label')).toEqual('patient.address') - expect(addressInput.prop('isEditable')).toBeTruthy() + const input = emailInput.find('input') + input.getDOMNode().value = expectedEmails[i].value + input.simulate('change') + }) - act(() => { - addressInput.prop('onChange')({ currentTarget: { value: expectedAddress } }) + const calledWith = [] as any + patient.emails.forEach((_, i) => { + const newEmails = [] as any + patient.emails.forEach((__, j) => { + if (j <= i) { + newEmails.push(expectedEmails[j]) + } else { + newEmails.push(patient.emails[j]) + } + }) + calledWith.push({ ...patient, emails: newEmails }) }) - expect(generalInformation.prop('onFieldChange')).toHaveBeenCalledWith( - 'address', - expectedAddress, - ) + expect(onFieldChange).toHaveBeenCalledTimes(calledWith.length) + expect(onFieldChange).toHaveBeenNthCalledWith(1, calledWith[0]) + // expect(onFieldChange).toHaveBeenNthCalledWith(2, calledWith[1]) }) - it('should render the approximate age if patient.isApproximateDateOfBirth is true', async () => { - patient.isApproximateDateOfBirth = true - await act(async () => { - wrapper = await mount( - - ) - , - ) - }) - - wrapper.update() + it('should render the addresses of the patient', () => { + patient.addresses.forEach((address, i) => { + const addressTextArea = wrapper.findWhere((w: any) => w.prop('name') === `address${i}`) + expect(addressTextArea.prop('value')).toEqual(address.value) + expect(addressTextArea.prop('isEditable')).toBeTruthy() - const approximateAgeInput = wrapper.findWhere((w: any) => w.prop('name') === 'approximateAge') - const generalInformation = wrapper.find(GeneralInformation) - expect(approximateAgeInput.prop('value')).toEqual('30') - expect(approximateAgeInput.prop('label')).toEqual('patient.approximateAge') - expect(approximateAgeInput.prop('isEditable')).toBeTruthy() + const textarea = addressTextArea.find('textarea') + textarea.getDOMNode().value = expectedAddresses[i].value + textarea.simulate('change') + }) - act(() => { - approximateAgeInput.prop('onChange')({ target: { value: '20' } }) + const calledWith = [] as any + patient.addresses.forEach((_, i) => { + const newAddresses = [] as any + patient.addresses.forEach((__, j) => { + if (j <= i) { + newAddresses.push(expectedAddresses[j]) + } else { + newAddresses.push(patient.addresses[j]) + } + }) + calledWith.push({ ...patient, addresses: newAddresses }) }) - expect(generalInformation.prop('onFieldChange')).toHaveBeenCalledWith( - 'dateOfBirth', - startOfDay(subYears(new Date(Date.now()), 20)).toISOString(), - ) + expect(onFieldChange).toHaveBeenCalledTimes(calledWith.length) + expect(onFieldChange).toHaveBeenNthCalledWith(1, calledWith[0]) + // expect(onFieldChange).toHaveBeenNthCalledWith(2, calledWith[1]) }) }) diff --git a/src/__tests__/patients/edit/EditPatient.test.tsx b/src/__tests__/patients/edit/EditPatient.test.tsx index 1eab177acb..671d2bc446 100644 --- a/src/__tests__/patients/edit/EditPatient.test.tsx +++ b/src/__tests__/patients/edit/EditPatient.test.tsx @@ -1,6 +1,5 @@ import '../../../__mocks__/matchMediaMock' -import { Button } from '@hospitalrun/components' import { subDays } from 'date-fns' import { mount } from 'enzyme' import { createMemoryHistory } from 'history' @@ -33,9 +32,9 @@ describe('Edit Patient', () => { type: 'charity', occupation: 'occupation', preferredLanguage: 'preferredLanguage', - phoneNumber: '123456789', - email: 'email@email.com', - address: 'address', + phoneNumbers: [{ value: '123456789' }], + emails: [{ value: 'email@email.com' }], + addresses: [{ value: 'address' }], code: 'P00001', dateOfBirth: subDays(new Date(), 2).toISOString(), index: 'givenName familyName suffixP00001', @@ -107,7 +106,7 @@ describe('Edit Patient', () => { wrapper.update() - const saveButton = wrapper.find(Button).at(0) + const saveButton = wrapper.find('.btn-save').at(0) const onClick = saveButton.prop('onClick') as any expect(saveButton.text().trim()).toEqual('actions.save') @@ -128,7 +127,7 @@ describe('Edit Patient', () => { wrapper.update() - const cancelButton = wrapper.find(Button).at(1) + const cancelButton = wrapper.find('.btn-cancel').at(1) const onClick = cancelButton.prop('onClick') as any expect(cancelButton.text().trim()).toEqual('actions.cancel') diff --git a/src/__tests__/patients/new/NewPatient.test.tsx b/src/__tests__/patients/new/NewPatient.test.tsx index f5237c1091..947c00e882 100644 --- a/src/__tests__/patients/new/NewPatient.test.tsx +++ b/src/__tests__/patients/new/NewPatient.test.tsx @@ -96,12 +96,12 @@ describe('New Patient', () => { const generalInformationForm = wrapper.find(GeneralInformation) act(() => { - generalInformationForm.prop('onFieldChange')('givenName', 'first') + generalInformationForm.prop('onChange')(patient) }) wrapper.update() - const saveButton = wrapper.find(components.Button).at(0) + const saveButton = wrapper.find('.btn-save').at(0) const onClick = saveButton.prop('onClick') as any expect(saveButton.text().trim()).toEqual('actions.save') @@ -125,12 +125,12 @@ describe('New Patient', () => { const generalInformationForm = wrapper.find(GeneralInformation) act(() => { - generalInformationForm.prop('onFieldChange')('givenName', 'first') + generalInformationForm.prop('onChange')(patient) }) wrapper.update() - const saveButton = wrapper.find(components.Button).at(0) + const saveButton = wrapper.find('.btn-save').at(0) const onClick = saveButton.prop('onClick') as any expect(saveButton.text().trim()).toEqual('actions.save') @@ -152,7 +152,7 @@ describe('New Patient', () => { wrapper = await setup() }) - const cancelButton = wrapper.find(components.Button).at(1) + const cancelButton = wrapper.find('.btn-cancel').at(0) const onClick = cancelButton.prop('onClick') as any expect(cancelButton.text().trim()).toEqual('actions.cancel') diff --git a/src/__tests__/patients/patient-slice.test.ts b/src/__tests__/patients/patient-slice.test.ts index 9ce0c2ddf6..829fb43d56 100644 --- a/src/__tests__/patients/patient-slice.test.ts +++ b/src/__tests__/patients/patient-slice.test.ts @@ -167,6 +167,7 @@ describe('patients slice', () => { const expectedPatient = { id: 'sliceId1', givenName: 'some name', + fullName: 'some name', } as Patient await store.dispatch(createPatient(expectedPatient)) @@ -251,13 +252,13 @@ describe('patients slice', () => { ) }) - it('should validate that the patient email is a valid email', async () => { + it('should validate that the patient phone number is a valid phone number', async () => { const store = mockStore() const expectedPatientId = 'sliceId10' const expectedPatient = { id: expectedPatientId, givenName: 'some given name', - phoneNumber: 'not a phone number', + phoneNumbers: [{ value: 'not a phone number' }], } as Patient const saveOrUpdateSpy = jest .spyOn(PatientRepository, 'saveOrUpdate') @@ -271,18 +272,18 @@ describe('patients slice', () => { expect(store.getActions()[1]).toEqual( createPatientError({ message: 'patient.errors.createPatientError', - phoneNumber: 'patient.errors.invalidPhoneNumber', + phoneNumbers: ['patient.errors.invalidPhoneNumber'], }), ) }) - it('should validate that the patient phone number is a valid phone number', async () => { + it('should validate that the patient email is a valid email', async () => { const store = mockStore() const expectedPatientId = 'sliceId10' const expectedPatient = { id: expectedPatientId, givenName: 'some given name', - phoneNumber: 'not a phone number', + emails: [{ value: 'not an email' }], } as Patient const saveOrUpdateSpy = jest .spyOn(PatientRepository, 'saveOrUpdate') @@ -296,7 +297,7 @@ describe('patients slice', () => { expect(store.getActions()[1]).toEqual( createPatientError({ message: 'patient.errors.createPatientError', - phoneNumber: 'patient.errors.invalidPhoneNumber', + emails: ['patient.errors.invalidEmail'], }), ) }) @@ -383,7 +384,11 @@ describe('patients slice', () => { it('should call the PatientRepository saveOrUpdate function with the correct data', async () => { const store = mockStore() const expectedPatientId = 'sliceId9' - const expectedPatient = { id: expectedPatientId, givenName: 'some name' } as Patient + const expectedPatient = { + id: expectedPatientId, + givenName: 'some name', + fullName: 'some name', + } as Patient jest.spyOn(PatientRepository, 'saveOrUpdate').mockResolvedValue(expectedPatient) await store.dispatch(updatePatient(expectedPatient)) diff --git a/src/components/input/SelectWithLableFormGroup.tsx b/src/components/input/SelectWithLableFormGroup.tsx index fff067037c..c61cf415c7 100644 --- a/src/components/input/SelectWithLableFormGroup.tsx +++ b/src/components/input/SelectWithLableFormGroup.tsx @@ -8,7 +8,7 @@ interface Option { interface Props { value: string - label: string + label?: string name: string isRequired?: boolean isEditable?: boolean @@ -33,7 +33,7 @@ const SelectWithLabelFormGroup = (props: Props) => { const id = `${name}Select` return (
-