diff --git a/packages/esm-patient-registration-app/src/patient-registration/patient-registration-hooks.ts b/packages/esm-patient-registration-app/src/patient-registration/patient-registration-hooks.ts index eca2463dee..0a9ec8176e 100644 --- a/packages/esm-patient-registration-app/src/patient-registration/patient-registration-hooks.ts +++ b/packages/esm-patient-registration-app/src/patient-registration/patient-registration-hooks.ts @@ -110,7 +110,7 @@ export function useInitialFormValues(patientUuid: string, isLocal: boolean): [Fo useEffect(() => { const fetchValues = async () => { - if (mpiPatient) { + if (mpiPatient?.data?.identifier) { const identifiers = await getIdentifierFieldValuesFromFhirPatient( mpiPatient.data, fieldConfigurations.identifier, diff --git a/packages/esm-patient-registration-app/src/patient-registration/patient-registration.test.tsx b/packages/esm-patient-registration-app/src/patient-registration/patient-registration.test.tsx index f2c93fd807..c662732cd0 100644 --- a/packages/esm-patient-registration-app/src/patient-registration/patient-registration.test.tsx +++ b/packages/esm-patient-registration-app/src/patient-registration/patient-registration.test.tsx @@ -1,7 +1,7 @@ import React from 'react'; import dayjs from 'dayjs'; import userEvent from '@testing-library/user-event'; -import { BrowserRouter as Router, useParams } from 'react-router-dom'; +import { BrowserRouter as Router, useParams, useLocation } from 'react-router-dom'; import { render, screen, within } from '@testing-library/react'; import { type FetchResponse, @@ -10,15 +10,17 @@ import { showSnackbar, useConfig, usePatient, + openmrsFetch, } from '@openmrs/esm-framework'; import { mockedAddressTemplate } from '__mocks__'; -import { mockPatient } from 'tools'; +import { mockPatient, mockOpenMRSIdentificationNumberIdType } from 'tools'; import { saveEncounter, savePatient } from './patient-registration.resource'; import { esmPatientRegistrationSchema, type RegistrationConfig } from '../config-schema'; import type { AddressTemplate, Encounter } from './patient-registration.types'; import { ResourcesContext } from '../offline.resources'; import { FormManager } from './form-manager'; import { PatientRegistration } from './patient-registration.component'; +import { useMpiPatient } from './mpi/mpi-patient.resource'; const mockSaveEncounter = jest.mocked(saveEncounter); const mockSavePatient = savePatient as jest.Mock; @@ -26,6 +28,11 @@ const mockShowSnackbar = jest.mocked(showSnackbar); const mockUseConfig = jest.mocked(useConfig); const mockUsePatient = jest.mocked(usePatient); const mockOpenmrsDatePicker = jest.mocked(OpenmrsDatePicker); +const mockUseMpiPatient = useMpiPatient as jest.Mock; + +jest.mock('./mpi/mpi-patient.resource', () => ({ + useMpiPatient: jest.fn(), +})); jest.mock('./field/field.resource', () => ({ useConcept: jest.fn().mockImplementation((uuid: string) => { @@ -86,7 +93,7 @@ jest.mock('./field/field.resource', () => ({ jest.mock('react-router-dom', () => ({ ...(jest.requireActual('react-router-dom') as any), - useLocation: () => ({ + useLocation: jest.fn().mockReturnValue({ pathname: 'openmrs/spa/patient-registration', }), useHistory: () => [], @@ -129,7 +136,7 @@ const mockResourcesContextValue = { let mockOpenmrsConfig: RegistrationConfig = { sections: ['demographics', 'contact'], sectionDefinitions: [ - { id: 'demographics', name: 'Demographics', fields: ['name', 'gender', 'dob'] }, + { id: 'demographics', name: 'Demographics', fields: ['name', 'gender', 'dob', 'id'] }, { id: 'contact', name: 'Contact Info', fields: ['address'] }, { id: 'relationships', name: 'Relationships', fields: ['relationship'] }, ], @@ -160,6 +167,9 @@ let mockOpenmrsConfig: RegistrationConfig = { label: 'Male', }, ], + identifier: [ + { identifierTypeSystem: 'MPI OpenMRS ID', identifierTypeUuid: '8d793bee-c2cc-11de-8d13-0010c6dffd0f' }, + ], address: { useAddressHierarchy: { enabled: true, @@ -174,7 +184,7 @@ let mockOpenmrsConfig: RegistrationConfig = { links: { submitButton: '#', }, - defaultPatientIdentifierTypes: [], + defaultPatientIdentifierTypes: ['8d793bee-c2cc-11de-8d13-0010c6dffd0f'], registrationObs: { encounterTypeUuid: null, encounterProviderRoleUuid: 'asdf', @@ -256,6 +266,18 @@ describe('Registering a new patient', () => { ...mockOpenmrsConfig, }); mockSavePatient.mockReturnValue({ data: { uuid: 'new-pt-uuid' }, ok: true }); + mockUseMpiPatient.mockReturnValue({ + isLoading: false, + patient: { data: null }, + error: undefined, + }); + (useLocation as jest.Mock).mockReturnValue({ + pathname: 'openmrs/spa/patient-registration', + state: undefined, + key: '', + search: '', + hash: '', + }); }); it('renders without crashing', () => { @@ -438,6 +460,121 @@ describe('Updating an existing patient record', () => { '1': { openMrsId: '100GEJ', }, + '2': { + mpiOpenMrsId: '100GEG', + }, + addNameInLocalLanguage: undefined, + additionalFamilyName: '', + additionalGivenName: '', + additionalMiddleName: '', + address: {}, + birthdate: new Date('1972-04-04T00:00:00.000Z'), + birthdateEstimated: false, + deathCause: '', + nonCodedCauseOfDeath: '', + deathDate: undefined, + deathTime: undefined, + deathTimeFormat: 'AM', + familyName: 'Smith', + gender: expect.stringMatching(/male/i), + givenName: 'Eric', + identifiers: {}, + isDead: false, + middleName: 'Johnson', + monthsEstimated: 0, + patientUuid: '8673ee4f-e2ab-4077-ba55-4980f408773e', + relationships: [], + telephoneNumber: '', + unidentifiedPatient: undefined, + yearsEstimated: 0, + }, + expect.anything(), + expect.anything(), + null, + undefined, + expect.anything(), + expect.anything(), + expect.anything(), + { patientSaved: false }, + expect.anything(), + ); + }); +}); + +describe('Import an MPI patient record', () => { + beforeEach(() => { + mockUseConfig.mockReturnValue(mockOpenmrsConfig); + mockSavePatient.mockReturnValue({ data: { uuid: 'new-pt-uuid' }, ok: true }); + + (useLocation as jest.Mock).mockReturnValue({ + pathname: 'openmrs/spa/patient-registration?sourceRecord=55', + state: undefined, + key: '', + search: '', + hash: '', + }); + }); + + it('fills patient demographics from MPI patient', async () => { + const user = userEvent.setup(); + mockSavePatient.mockResolvedValue({} as FetchResponse); + + mockUsePatient.mockReturnValue({ + isLoading: false, + patient: null, + patientUuid: null, + error: null, + }); + + mockUseMpiPatient.mockReturnValue({ + isLoading: false, + patient: { data: mockPatient }, + error: undefined, + }); + + const mockOpenmrsFetch = openmrsFetch as jest.Mock; + const mockResponse = { status: 200, data: mockOpenMRSIdentificationNumberIdType }; + mockOpenmrsFetch.mockResolvedValue(mockResponse); + + render(, { wrapper: Wrapper }); + expect(mockOpenmrsFetch.mock.calls[0][0]).toEqual( + `/ws/rest/v1/patientidentifiertype/8d793bee-c2cc-11de-8d13-0010c6dffd0f`, + ); + + const givenNameInput: HTMLInputElement = screen.getByLabelText(/First Name/); + const familyNameInput: HTMLInputElement = screen.getByLabelText(/Family Name/); + const middleNameInput: HTMLInputElement = screen.getByLabelText(/Middle Name/); + const dateOfBirthInput: HTMLInputElement = screen.getByLabelText(/Date of Birth/i); + const genderInput: HTMLInputElement = screen.getByLabelText(/Male/); + + // assert initial values + expect(givenNameInput.value).toBe('John'); + expect(familyNameInput.value).toBe('Wilson'); + expect(middleNameInput.value).toBeFalsy(); + expect(dateOfBirthInput.value).toBe('04/04/1972'); + expect(genderInput.value).toBe('male'); + + // do some edits + await user.clear(givenNameInput); + await user.clear(middleNameInput); + await user.clear(familyNameInput); + await user.type(givenNameInput, 'Eric'); + await user.type(middleNameInput, 'Johnson'); + await user.type(familyNameInput, 'Smith'); + await user.click(screen.getByText(/Register patient/i)); + + expect(mockSavePatient).toHaveBeenCalledWith( + false, + { + '0': { + oldIdentificationNumber: '100732HE', + }, + '1': { + openMrsId: '100GEJ', + }, + '2': { + mpiOpenMrsId: '100GEG', + }, addNameInLocalLanguage: undefined, additionalFamilyName: '', additionalGivenName: '', diff --git a/tools/index.ts b/tools/index.ts index e3600e40ad..76f5317457 100644 --- a/tools/index.ts +++ b/tools/index.ts @@ -1,6 +1,7 @@ export { getByTextWithMarkup, mockPatient, + mockOpenMRSIdentificationNumberIdType, mockPatientWithLongName, mockPatientWithoutFormattedName, patientChartBasePath, diff --git a/tools/test-utils.tsx b/tools/test-utils.tsx index cf0d50e4f1..b72733ac83 100644 --- a/tools/test-utils.tsx +++ b/tools/test-utils.tsx @@ -28,6 +28,7 @@ function getByTextWithMarkup(text: RegExp | string) { try { return screen.getByText((content, node) => { const hasText = (node: Element) => node.textContent === text || node.textContent.match(text); + // eslint-disable-next-line testing-library/no-node-access const childrenDontHaveText = Array.from(node.children).every((child) => !hasText(child as HTMLElement)); return hasText(node) && childrenDontHaveText; }); @@ -36,6 +37,33 @@ function getByTextWithMarkup(text: RegExp | string) { } } +const mockOpenMRSIdentificationNumberIdType = { + uuid: '8d793bee-c2cc-11de-8d13-0010c6dffd0f', + display: 'OpenMRS Identification Number', + name: 'OpenMRS Identification Number', + description: 'Unique number used in OpenMRS', + format: '', + formatDescription: null, + required: false, + validator: 'org.openmrs.patient.impl.LuhnIdentifierValidator', + locationBehavior: null, + uniquenessBehavior: null, + retired: false, + links: [ + { + rel: 'self', + uri: 'http://localhost/openmrs/ws/rest/v1/patientidentifiertype/8d793bee-c2cc-11de-8d13-0010c6dffd0f', + resourceAlias: 'patientidentifiertype', + }, + { + rel: 'full', + uri: 'http://localhost/openmrs/ws/rest/v1/patientidentifiertype/8d793bee-c2cc-11de-8d13-0010c6dffd0f?v=full', + resourceAlias: 'patientidentifiertype', + }, + ], + resourceVersion: '2.0', +}; + const mockPatient = { resourceType: 'Patient', id: '8673ee4f-e2ab-4077-ba55-4980f408773e', @@ -62,6 +90,12 @@ const mockPatient = { system: 'OpenMRS ID', value: '100GEJ', }, + { + id: '2f0ad7a1-430f-4397-b571-59ea654a52db', + use: 'official', + system: 'MPI OpenMRS ID', + value: '100GEG', + }, ], active: true, name: [ @@ -111,6 +145,7 @@ export { waitForLoadingToFinish, getByTextWithMarkup, mockPatient, + mockOpenMRSIdentificationNumberIdType, mockPatientWithLongName, mockPatientWithoutFormattedName, patientChartBasePath,