@kenyaemr/esm-patient-registration-app 5.2.2 → 6.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/130.js +1 -1
- package/dist/130.js.map +1 -1
- package/dist/271.js +1 -0
- package/dist/319.js +1 -1
- package/dist/330.js +1 -1
- package/dist/460.js +1 -1
- package/dist/537.js +1 -1
- package/dist/574.js +1 -1
- package/dist/59.js +1 -1
- package/dist/59.js.map +1 -1
- package/dist/619.js +1 -0
- package/dist/619.js.map +1 -0
- package/dist/644.js +1 -0
- package/dist/735.js +1 -1
- package/dist/757.js +1 -1
- package/dist/784.js +1 -1
- package/dist/788.js +1 -1
- package/dist/807.js +1 -1
- package/dist/833.js +1 -1
- package/dist/895.js +2 -0
- package/dist/{388.js.LICENSE.txt → 895.js.LICENSE.txt} +4 -2
- package/dist/895.js.map +1 -0
- package/dist/{openmrs-esm-patient-registration-app.js → kenyaemr-esm-patient-registration-app.js} +1 -1
- package/dist/{openmrs-esm-patient-registration-app.js.buildmanifest.json → kenyaemr-esm-patient-registration-app.js.buildmanifest.json} +117 -73
- package/dist/kenyaemr-esm-patient-registration-app.js.map +1 -0
- package/dist/main.js +1 -1
- package/dist/main.js.LICENSE.txt +4 -2
- package/dist/main.js.map +1 -1
- package/dist/routes.json +1 -1
- package/package.json +6 -5
- package/src/add-patient-link.test.tsx +9 -7
- package/src/config-schema.ts +31 -38
- package/src/constants.ts +1 -1
- package/src/offline.resources.ts +13 -18
- package/src/offline.ts +8 -6
- package/src/patient-registration/before-save-prompt.tsx +2 -1
- package/src/patient-registration/field/__mocks__/field.resource.ts +1 -1
- package/src/patient-registration/field/address/address-field.component.tsx +5 -4
- package/src/patient-registration/field/address/address-hierarchy.resource.tsx +2 -2
- package/src/patient-registration/field/address/address-search.component.tsx +1 -14
- package/src/patient-registration/field/address/custom-address-field.component.tsx +1 -1
- package/src/patient-registration/field/address/tests/address-hierarchy.test.tsx +3 -3
- package/src/patient-registration/field/address/tests/address-search-component.test.tsx +14 -7
- package/src/patient-registration/field/custom-field.component.tsx +1 -1
- package/src/patient-registration/field/dob/dob.component.tsx +2 -2
- package/src/patient-registration/field/dob/dob.test.tsx +0 -3
- package/src/patient-registration/field/field.component.tsx +4 -1
- package/src/patient-registration/field/field.resource.ts +4 -4
- package/src/patient-registration/field/field.test.tsx +98 -95
- package/src/patient-registration/field/gender/gender-field.component.tsx +4 -4
- package/src/patient-registration/field/gender/gender-field.test.tsx +7 -14
- package/src/patient-registration/field/id/id-field.component.tsx +6 -6
- package/src/patient-registration/field/id/id-field.test.tsx +9 -7
- package/src/patient-registration/field/id/identifier-selection-overlay.component.tsx +1 -1
- package/src/patient-registration/field/name/name-field.component.tsx +1 -1
- package/src/patient-registration/field/obs/obs-field.component.tsx +35 -33
- package/src/patient-registration/field/obs/obs-field.test.tsx +106 -28
- package/src/patient-registration/field/person-attributes/coded-attributes.component.tsx +1 -1
- package/src/patient-registration/field/person-attributes/coded-person-attribute-field.component.tsx +70 -24
- package/src/patient-registration/field/person-attributes/coded-person-attribute-field.test.tsx +54 -30
- package/src/patient-registration/field/person-attributes/person-attribute-field.component.tsx +4 -3
- package/src/patient-registration/field/person-attributes/person-attribute-field.test.tsx +1 -1
- package/src/patient-registration/field/person-attributes/{person-attributes.resource.tsx → person-attributes.resource.ts} +3 -3
- package/src/patient-registration/field/person-attributes/text-person-attribute-field.component.tsx +1 -1
- package/src/patient-registration/field/phone/phone-field.component.tsx +16 -0
- package/src/patient-registration/form-manager.test.ts +1 -1
- package/src/patient-registration/form-manager.ts +22 -24
- package/src/patient-registration/input/basic-input/select/select-input.test.tsx +3 -3
- package/src/patient-registration/input/custom-input/autosuggest/autosuggest.component.tsx +5 -5
- package/src/patient-registration/input/custom-input/autosuggest/autosuggest.test.tsx +70 -58
- package/src/patient-registration/input/custom-input/identifier/identifier-input.component.tsx +2 -2
- package/src/patient-registration/input/custom-input/identifier/identifier-input.test.tsx +2 -5
- package/src/patient-registration/input/custom-input/identifier/utils.ts +1 -1
- package/src/patient-registration/input/dummy-data/dummy-data-input.component.tsx +1 -1
- package/src/patient-registration/input/dummy-data/dummy-data-input.test.tsx +6 -6
- package/src/patient-registration/patient-registration-context.ts +3 -4
- package/src/patient-registration/patient-registration-hooks.ts +25 -20
- package/src/patient-registration/patient-registration-utils.ts +7 -7
- package/src/patient-registration/patient-registration.component.tsx +20 -10
- package/src/patient-registration/patient-registration.resource.test.tsx +3 -3
- package/src/patient-registration/{patient-registration.resource.tsx → patient-registration.resource.ts} +15 -15
- package/src/patient-registration/patient-registration.test.tsx +270 -251
- package/src/patient-registration/{patient-registration.types.tsx → patient-registration.types.ts} +12 -3
- package/src/patient-registration/section/death-info/death-info-section.test.tsx +33 -45
- package/src/patient-registration/section/demographics/demographics-section.test.tsx +1 -2
- package/src/patient-registration/section/generic-section.component.tsx +1 -1
- package/src/patient-registration/section/patient-relationships/relationships-section.component.tsx +3 -3
- package/src/patient-registration/section/patient-relationships/relationships-section.test.tsx +17 -5
- package/src/patient-registration/section/patient-relationships/relationships.resource.tsx +4 -4
- package/src/patient-registration/section/section-wrapper.component.tsx +1 -1
- package/src/patient-registration/section/section.component.tsx +1 -1
- package/src/patient-registration/validation/patient-registration-validation.test.tsx +140 -126
- package/src/patient-registration/validation/patient-registration-validation.tsx +54 -46
- package/src/patient-verification/patient-verification-hook.tsx +13 -4
- package/src/patient-verification/patient-verification-utils.ts +20 -12
- package/src/patient-verification/patient-verification.component.tsx +13 -6
- package/src/patient-verification/patient-verification.scss +0 -1
- package/src/patient-verification/verification-modal/confirm-prompt.component.tsx +2 -11
- package/src/routes.json +1 -0
- package/src/widgets/cancel-patient-edit.test.tsx +7 -4
- package/src/widgets/delete-identifier-confirmation-modal.test.tsx +7 -4
- package/src/widgets/display-photo.test.tsx +1 -1
- package/src/widgets/edit-patient-details-button.test.tsx +12 -7
- package/translations/am.json +30 -14
- package/translations/ar.json +30 -14
- package/translations/en.json +11 -11
- package/translations/es.json +34 -22
- package/translations/fr.json +48 -40
- package/translations/he.json +22 -2
- package/translations/km.json +22 -2
- package/translations/zh.json +97 -0
- package/translations/zh_CN.json +97 -0
- package/tsconfig.json +1 -1
- package/__mocks__/autogenerationoptions.mock.ts +0 -34
- package/dist/388.js +0 -2
- package/dist/388.js.map +0 -1
- package/dist/598.js +0 -1
- package/dist/598.js.map +0 -1
- package/dist/openmrs-esm-patient-registration-app.js.map +0 -1
- package/src/patient-registration/field/__mocks__/identifier-types.mock.ts +0 -76
- package/src/patient-registration/field/__mocks__/identifiers.mock.ts +0 -27
- package/src/patient-registration/field/address/tests/mocks.ts +0 -98
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { BrowserRouter as Router, useParams } from 'react-router-dom';
|
|
3
|
-
import { render, screen,
|
|
3
|
+
import { render, screen, within } from '@testing-library/react';
|
|
4
4
|
import userEvent from '@testing-library/user-event';
|
|
5
5
|
import { showSnackbar, useConfig, usePatient } from '@openmrs/esm-framework';
|
|
6
|
+
import type { AddressTemplate, Encounter } from './patient-registration.types';
|
|
7
|
+
import { type RegistrationConfig } from '../config-schema';
|
|
6
8
|
import { FormManager } from './form-manager';
|
|
7
|
-
import {
|
|
8
|
-
import type { Encounter } from './patient-registration.types';
|
|
9
|
-
import { Resources, ResourcesContext } from '../offline.resources';
|
|
9
|
+
import { ResourcesContext } from '../offline.resources';
|
|
10
10
|
import { PatientRegistration } from './patient-registration.component';
|
|
11
|
-
import {
|
|
12
|
-
import { mockedAddressTemplate } from '
|
|
13
|
-
import { mockPatient } from '
|
|
11
|
+
import { saveEncounter, savePatient } from './patient-registration.resource';
|
|
12
|
+
import { mockedAddressTemplate } from '__mocks__';
|
|
13
|
+
import { mockPatient } from 'tools';
|
|
14
14
|
|
|
15
15
|
const mockedUseConfig = useConfig as jest.Mock;
|
|
16
16
|
const mockedUsePatient = usePatient as jest.Mock;
|
|
@@ -18,7 +18,62 @@ const mockedSaveEncounter = saveEncounter as jest.Mock;
|
|
|
18
18
|
const mockedSavePatient = savePatient as jest.Mock;
|
|
19
19
|
const mockedShowSnackbar = showSnackbar as jest.Mock;
|
|
20
20
|
|
|
21
|
-
jest.
|
|
21
|
+
jest.mock('./field/field.resource', () => ({
|
|
22
|
+
useConcept: jest.fn().mockImplementation((uuid: string) => {
|
|
23
|
+
let data;
|
|
24
|
+
if (uuid == 'weight-uuid') {
|
|
25
|
+
data = {
|
|
26
|
+
uuid: 'weight-uuid',
|
|
27
|
+
display: 'Weight (kg)',
|
|
28
|
+
datatype: { display: 'Numeric', uuid: 'num' },
|
|
29
|
+
answers: [],
|
|
30
|
+
setMembers: [],
|
|
31
|
+
};
|
|
32
|
+
} else if (uuid == 'chief-complaint-uuid') {
|
|
33
|
+
data = {
|
|
34
|
+
uuid: 'chief-complaint-uuid',
|
|
35
|
+
display: 'Chief Complaint',
|
|
36
|
+
datatype: { display: 'Text', uuid: 'txt' },
|
|
37
|
+
answers: [],
|
|
38
|
+
setMembers: [],
|
|
39
|
+
};
|
|
40
|
+
} else if (uuid == 'nationality-uuid') {
|
|
41
|
+
data = {
|
|
42
|
+
uuid: 'nationality-uuid',
|
|
43
|
+
display: 'Nationality',
|
|
44
|
+
datatype: { display: 'Coded', uuid: 'cdd' },
|
|
45
|
+
answers: [
|
|
46
|
+
{ display: 'USA', uuid: 'usa' },
|
|
47
|
+
{ display: 'Mexico', uuid: 'mex' },
|
|
48
|
+
],
|
|
49
|
+
setMembers: [],
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
return {
|
|
53
|
+
data: data ?? null,
|
|
54
|
+
isLoading: !data,
|
|
55
|
+
};
|
|
56
|
+
}),
|
|
57
|
+
useConceptAnswers: jest.fn().mockImplementation((uuid: string) => {
|
|
58
|
+
if (uuid == 'nationality-uuid') {
|
|
59
|
+
return {
|
|
60
|
+
data: [
|
|
61
|
+
{ display: 'USA', uuid: 'usa' },
|
|
62
|
+
{ display: 'Mexico', uuid: 'mex' },
|
|
63
|
+
],
|
|
64
|
+
isLoading: false,
|
|
65
|
+
};
|
|
66
|
+
} else if (uuid == 'other-countries-uuid') {
|
|
67
|
+
return {
|
|
68
|
+
data: [
|
|
69
|
+
{ display: 'Kenya', uuid: 'ke' },
|
|
70
|
+
{ display: 'Uganda', uuid: 'ug' },
|
|
71
|
+
],
|
|
72
|
+
isLoading: false,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
}),
|
|
76
|
+
}));
|
|
22
77
|
|
|
23
78
|
jest.mock('@openmrs/esm-framework', () => {
|
|
24
79
|
const originalModule = jest.requireActual('@openmrs/esm-framework');
|
|
@@ -29,9 +84,6 @@ jest.mock('@openmrs/esm-framework', () => {
|
|
|
29
84
|
};
|
|
30
85
|
});
|
|
31
86
|
|
|
32
|
-
// Mock field.resource using the manual mock (in __mocks__)
|
|
33
|
-
jest.mock('./field/field.resource');
|
|
34
|
-
|
|
35
87
|
jest.mock('react-router-dom', () => ({
|
|
36
88
|
...(jest.requireActual('react-router-dom') as any),
|
|
37
89
|
useLocation: () => ({
|
|
@@ -62,7 +114,7 @@ jest.mock('@openmrs/esm-framework', () => {
|
|
|
62
114
|
});
|
|
63
115
|
|
|
64
116
|
const mockResourcesContextValue = {
|
|
65
|
-
addressTemplate: mockedAddressTemplate,
|
|
117
|
+
addressTemplate: mockedAddressTemplate as AddressTemplate,
|
|
66
118
|
currentSession: {
|
|
67
119
|
authenticated: true,
|
|
68
120
|
sessionId: 'JSESSION',
|
|
@@ -70,7 +122,7 @@ const mockResourcesContextValue = {
|
|
|
70
122
|
},
|
|
71
123
|
relationshipTypes: [],
|
|
72
124
|
identifierTypes: [],
|
|
73
|
-
}
|
|
125
|
+
};
|
|
74
126
|
|
|
75
127
|
let mockOpenmrsConfig: RegistrationConfig = {
|
|
76
128
|
sections: ['demographics', 'contact'],
|
|
@@ -99,9 +151,8 @@ let mockOpenmrsConfig: RegistrationConfig = {
|
|
|
99
151
|
},
|
|
100
152
|
gender: [
|
|
101
153
|
{
|
|
102
|
-
value: '
|
|
154
|
+
value: 'male',
|
|
103
155
|
label: 'Male',
|
|
104
|
-
id: 'male',
|
|
105
156
|
},
|
|
106
157
|
],
|
|
107
158
|
address: {
|
|
@@ -138,6 +189,7 @@ configWithObs.fieldDefinitions = [
|
|
|
138
189
|
placeholder: '',
|
|
139
190
|
validation: { required: false, matches: null },
|
|
140
191
|
answerConceptSetUuid: null,
|
|
192
|
+
customConceptAnswers: [],
|
|
141
193
|
},
|
|
142
194
|
{
|
|
143
195
|
id: 'chief complaint',
|
|
@@ -147,6 +199,7 @@ configWithObs.fieldDefinitions = [
|
|
|
147
199
|
placeholder: '',
|
|
148
200
|
validation: { required: false, matches: null },
|
|
149
201
|
answerConceptSetUuid: null,
|
|
202
|
+
customConceptAnswers: [],
|
|
150
203
|
},
|
|
151
204
|
{
|
|
152
205
|
id: 'nationality',
|
|
@@ -156,8 +209,10 @@ configWithObs.fieldDefinitions = [
|
|
|
156
209
|
placeholder: '',
|
|
157
210
|
validation: { required: false, matches: null },
|
|
158
211
|
answerConceptSetUuid: null,
|
|
212
|
+
customConceptAnswers: [],
|
|
159
213
|
},
|
|
160
214
|
];
|
|
215
|
+
|
|
161
216
|
configWithObs.sectionDefinitions?.push({
|
|
162
217
|
id: 'custom',
|
|
163
218
|
name: 'Custom',
|
|
@@ -182,271 +237,235 @@ const fillRequiredFields = async () => {
|
|
|
182
237
|
user.click(genderInput);
|
|
183
238
|
};
|
|
184
239
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
240
|
+
function Wrapper({ children }) {
|
|
241
|
+
return (
|
|
242
|
+
<ResourcesContext.Provider value={mockResourcesContextValue}>
|
|
243
|
+
<Router>{children}</Router>
|
|
244
|
+
</ResourcesContext.Provider>
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
describe('Registering a new patient', () => {
|
|
249
|
+
beforeEach(() => {
|
|
250
|
+
mockedUseConfig.mockReturnValue(mockOpenmrsConfig);
|
|
251
|
+
mockedSavePatient.mockReturnValue({ data: { uuid: 'new-pt-uuid' }, ok: true });
|
|
252
|
+
mockedSaveEncounter.mockClear();
|
|
253
|
+
mockedShowSnackbar.mockClear();
|
|
254
|
+
jest.clearAllMocks();
|
|
255
|
+
});
|
|
194
256
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
<Router>
|
|
199
|
-
<PatientRegistration isOffline={false} savePatientForm={jest.fn()} />
|
|
200
|
-
</Router>
|
|
201
|
-
,
|
|
202
|
-
</ResourcesContext.Provider>,
|
|
203
|
-
);
|
|
204
|
-
});
|
|
257
|
+
it('renders without crashing', () => {
|
|
258
|
+
render(<PatientRegistration isOffline={false} savePatientForm={jest.fn()} />, { wrapper: Wrapper });
|
|
259
|
+
});
|
|
205
260
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
<ResourcesContext.Provider value={mockResourcesContextValue}>
|
|
209
|
-
<Router>
|
|
210
|
-
<PatientRegistration isOffline={false} savePatientForm={jest.fn()} />
|
|
211
|
-
</Router>
|
|
212
|
-
</ResourcesContext.Provider>,
|
|
213
|
-
);
|
|
214
|
-
await waitFor(() => expect(screen.getByLabelText(/Demographics Section/)).not.toBeNull());
|
|
215
|
-
expect(screen.getByLabelText(/Contact Info Section/)).not.toBeNull();
|
|
216
|
-
});
|
|
261
|
+
it('has the expected sections', async () => {
|
|
262
|
+
render(<PatientRegistration isOffline={false} savePatientForm={jest.fn()} />, { wrapper: Wrapper });
|
|
217
263
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
);
|
|
228
|
-
|
|
229
|
-
await fillRequiredFields();
|
|
230
|
-
await user.click(await screen.findByText('Register Patient'));
|
|
231
|
-
await waitFor(() => {
|
|
232
|
-
expect(mockedSavePatient).toHaveBeenCalledWith(
|
|
233
|
-
expect.objectContaining({
|
|
234
|
-
identifiers: [], //TODO when the identifer story is finished: { identifier: '', identifierType: '05a29f94-c0ed-11e2-94be-8c13b969e334', location: '' },
|
|
235
|
-
person: {
|
|
236
|
-
addresses: expect.arrayContaining([expect.any(Object)]),
|
|
237
|
-
attributes: [],
|
|
238
|
-
birthdate: '1993-8-2',
|
|
239
|
-
birthdateEstimated: false,
|
|
240
|
-
gender: 'M',
|
|
241
|
-
names: [{ givenName: 'Paul', middleName: '', familyName: 'Gaihre', preferred: true, uuid: undefined }],
|
|
242
|
-
dead: false,
|
|
243
|
-
uuid: expect.anything(),
|
|
244
|
-
},
|
|
245
|
-
uuid: expect.anything(),
|
|
246
|
-
}),
|
|
247
|
-
undefined,
|
|
248
|
-
);
|
|
249
|
-
});
|
|
264
|
+
expect(screen.getByLabelText(/Demographics Section/)).not.toBeNull();
|
|
265
|
+
expect(screen.getByLabelText(/Contact Info Section/)).not.toBeNull();
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
it('saves the patient without extra info', async () => {
|
|
269
|
+
const user = userEvent.setup();
|
|
270
|
+
|
|
271
|
+
render(<PatientRegistration isOffline={false} savePatientForm={FormManager.savePatientFormOnline} />, {
|
|
272
|
+
wrapper: Wrapper,
|
|
250
273
|
});
|
|
251
274
|
|
|
252
|
-
|
|
253
|
-
|
|
275
|
+
await fillRequiredFields();
|
|
276
|
+
await user.click(await screen.findByText('Register Patient'));
|
|
277
|
+
expect(mockedSavePatient).toHaveBeenCalledWith(
|
|
278
|
+
expect.objectContaining({
|
|
279
|
+
identifiers: [], //TODO when the identifer story is finished: { identifier: '', identifierType: '05a29f94-c0ed-11e2-94be-8c13b969e334', location: '' },
|
|
280
|
+
person: {
|
|
281
|
+
addresses: expect.arrayContaining([expect.any(Object)]),
|
|
282
|
+
attributes: [],
|
|
283
|
+
birthdate: '1993-8-2',
|
|
284
|
+
birthdateEstimated: false,
|
|
285
|
+
gender: expect.stringMatching(/^M$/),
|
|
286
|
+
names: [{ givenName: 'Paul', middleName: '', familyName: 'Gaihre', preferred: true, uuid: undefined }],
|
|
287
|
+
dead: false,
|
|
288
|
+
uuid: expect.anything(),
|
|
289
|
+
},
|
|
290
|
+
uuid: expect.anything(),
|
|
291
|
+
}),
|
|
292
|
+
undefined,
|
|
293
|
+
);
|
|
294
|
+
});
|
|
254
295
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
<ResourcesContext.Provider value={mockResourcesContextValue}>
|
|
258
|
-
<Router>
|
|
259
|
-
<PatientRegistration isOffline={false} savePatientForm={mockedSavePatientForm} />
|
|
260
|
-
</Router>
|
|
261
|
-
</ResourcesContext.Provider>,
|
|
262
|
-
);
|
|
296
|
+
it('should not save the patient if validation fails', async () => {
|
|
297
|
+
const user = userEvent.setup();
|
|
263
298
|
|
|
264
|
-
|
|
299
|
+
const mockedSavePatientForm = jest.fn();
|
|
300
|
+
render(<PatientRegistration isOffline={false} savePatientForm={mockedSavePatientForm} />, { wrapper: Wrapper });
|
|
265
301
|
|
|
266
|
-
|
|
267
|
-
await user.click(screen.getByText('Register Patient'));
|
|
302
|
+
const givenNameInput = (await screen.findByLabelText('First Name')) as HTMLInputElement;
|
|
268
303
|
|
|
269
|
-
|
|
270
|
-
|
|
304
|
+
await user.type(givenNameInput, '5');
|
|
305
|
+
await user.click(screen.getByText('Register Patient'));
|
|
271
306
|
|
|
272
|
-
|
|
273
|
-
|
|
307
|
+
expect(mockedSavePatientForm).not.toHaveBeenCalled();
|
|
308
|
+
});
|
|
274
309
|
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
</ResourcesContext.Provider>,
|
|
284
|
-
);
|
|
285
|
-
|
|
286
|
-
await fillRequiredFields();
|
|
287
|
-
const customSection = screen.getByLabelText('Custom Section');
|
|
288
|
-
const weight = within(customSection).getByLabelText('Weight (kg) (optional)');
|
|
289
|
-
await user.type(weight, '50');
|
|
290
|
-
const complaint = within(customSection).getByLabelText('Chief Complaint (optional)');
|
|
291
|
-
await user.type(complaint, 'sad');
|
|
292
|
-
const nationality = within(customSection).getByLabelText('Nationality');
|
|
293
|
-
await user.selectOptions(nationality, 'USA');
|
|
294
|
-
|
|
295
|
-
await user.click(screen.getByText('Register Patient'));
|
|
296
|
-
|
|
297
|
-
await waitFor(() => expect(mockedSavePatient).toHaveBeenCalled());
|
|
298
|
-
await waitFor(() =>
|
|
299
|
-
expect(mockedSaveEncounter).toHaveBeenCalledWith(
|
|
300
|
-
expect.objectContaining<Partial<Encounter>>({
|
|
301
|
-
encounterType: 'reg-enc-uuid',
|
|
302
|
-
patient: 'new-pt-uuid',
|
|
303
|
-
obs: [
|
|
304
|
-
{ concept: 'weight-uuid', value: 50 },
|
|
305
|
-
{ concept: 'chief-complaint-uuid', value: 'sad' },
|
|
306
|
-
{ concept: 'nationality-uuid', value: 'usa' },
|
|
307
|
-
],
|
|
308
|
-
}),
|
|
309
|
-
),
|
|
310
|
-
);
|
|
310
|
+
it('renders and saves registration obs', async () => {
|
|
311
|
+
const user = userEvent.setup();
|
|
312
|
+
|
|
313
|
+
mockedSaveEncounter.mockResolvedValue({});
|
|
314
|
+
mockedUseConfig.mockReturnValue(configWithObs);
|
|
315
|
+
|
|
316
|
+
render(<PatientRegistration isOffline={false} savePatientForm={FormManager.savePatientFormOnline} />, {
|
|
317
|
+
wrapper: Wrapper,
|
|
311
318
|
});
|
|
312
319
|
|
|
313
|
-
|
|
314
|
-
|
|
320
|
+
await fillRequiredFields();
|
|
321
|
+
const customSection = screen.getByLabelText('Custom Section');
|
|
322
|
+
const weight = within(customSection).getByLabelText('Weight (kg) (optional)');
|
|
323
|
+
await user.type(weight, '50');
|
|
324
|
+
const complaint = within(customSection).getByLabelText('Chief Complaint (optional)');
|
|
325
|
+
await user.type(complaint, 'sad');
|
|
326
|
+
const nationality = within(customSection).getByLabelText('Nationality');
|
|
327
|
+
await user.selectOptions(nationality, 'USA');
|
|
328
|
+
|
|
329
|
+
await user.click(screen.getByText('Register Patient'));
|
|
330
|
+
|
|
331
|
+
expect(mockedSavePatient).toHaveBeenCalled();
|
|
332
|
+
|
|
333
|
+
expect(mockedSaveEncounter).toHaveBeenCalledWith(
|
|
334
|
+
expect.objectContaining<Partial<Encounter>>({
|
|
335
|
+
encounterType: 'reg-enc-uuid',
|
|
336
|
+
patient: 'new-pt-uuid',
|
|
337
|
+
obs: [
|
|
338
|
+
{ concept: 'weight-uuid', value: 50 },
|
|
339
|
+
{ concept: 'chief-complaint-uuid', value: 'sad' },
|
|
340
|
+
{ concept: 'nationality-uuid', value: 'usa' },
|
|
341
|
+
],
|
|
342
|
+
}),
|
|
343
|
+
);
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
it('retries saving registration obs after a failed attempt', async () => {
|
|
347
|
+
const user = userEvent.setup();
|
|
315
348
|
|
|
316
|
-
|
|
349
|
+
mockedUseConfig.mockReturnValue(configWithObs);
|
|
350
|
+
|
|
351
|
+
render(<PatientRegistration isOffline={false} savePatientForm={FormManager.savePatientFormOnline} />, {
|
|
352
|
+
wrapper: Wrapper,
|
|
353
|
+
});
|
|
317
354
|
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
</Router>
|
|
323
|
-
</ResourcesContext.Provider>,
|
|
324
|
-
);
|
|
355
|
+
await fillRequiredFields();
|
|
356
|
+
const customSection = screen.getByLabelText('Custom Section');
|
|
357
|
+
const weight = within(customSection).getByLabelText('Weight (kg) (optional)');
|
|
358
|
+
await user.type(weight, '-999');
|
|
325
359
|
|
|
326
|
-
|
|
327
|
-
const customSection = screen.getByLabelText('Custom Section');
|
|
328
|
-
const weight = within(customSection).getByLabelText('Weight (kg) (optional)');
|
|
329
|
-
await user.type(weight, '-999');
|
|
360
|
+
mockedSaveEncounter.mockRejectedValue({ status: 400, responseBody: { error: { message: 'an error message' } } });
|
|
330
361
|
|
|
331
|
-
|
|
362
|
+
const registerPatientButton = screen.getByText('Register Patient');
|
|
332
363
|
|
|
333
|
-
|
|
364
|
+
await user.click(registerPatientButton);
|
|
334
365
|
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
await waitFor(() =>
|
|
338
|
-
expect(mockedShowSnackbar).toHaveBeenCalledWith(expect.objectContaining({ subtitle: 'an error message' })),
|
|
339
|
-
);
|
|
366
|
+
expect(mockedSavePatient).toHaveBeenCalledTimes(1);
|
|
367
|
+
expect(mockedSaveEncounter).toHaveBeenCalledTimes(1);
|
|
340
368
|
|
|
369
|
+
expect(mockedShowSnackbar).toHaveBeenCalledWith(expect.objectContaining({ subtitle: 'an error message' })),
|
|
341
370
|
mockedSaveEncounter.mockResolvedValue({});
|
|
342
371
|
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
);
|
|
349
|
-
});
|
|
372
|
+
await user.click(registerPatientButton);
|
|
373
|
+
expect(mockedSavePatient).toHaveBeenCalledTimes(2);
|
|
374
|
+
expect(mockedSaveEncounter).toHaveBeenCalledTimes(2);
|
|
375
|
+
|
|
376
|
+
expect(mockedShowSnackbar).toHaveBeenCalledWith(expect.objectContaining({ kind: 'success' }));
|
|
350
377
|
});
|
|
378
|
+
});
|
|
351
379
|
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
380
|
+
describe('Updating an existing patient record', () => {
|
|
381
|
+
beforeEach(() => {
|
|
382
|
+
mockedUseConfig.mockReturnValue(mockOpenmrsConfig);
|
|
383
|
+
mockedSavePatient.mockReturnValue({ data: { uuid: 'new-pt-uuid' }, ok: true });
|
|
384
|
+
mockedSaveEncounter.mockClear();
|
|
385
|
+
mockedShowSnackbar.mockClear();
|
|
386
|
+
jest.clearAllMocks();
|
|
387
|
+
});
|
|
360
388
|
|
|
361
|
-
|
|
362
|
-
|
|
389
|
+
it('edits patient demographics', async () => {
|
|
390
|
+
const user = userEvent.setup();
|
|
363
391
|
|
|
364
|
-
|
|
392
|
+
mockedSavePatient.mockResolvedValue({});
|
|
365
393
|
|
|
366
|
-
|
|
394
|
+
const mockedUseParams = useParams as jest.Mock;
|
|
367
395
|
|
|
368
|
-
|
|
396
|
+
mockedUseParams.mockReturnValue({ patientUuid: mockPatient.id });
|
|
369
397
|
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
});
|
|
376
|
-
|
|
377
|
-
render(
|
|
378
|
-
<ResourcesContext.Provider value={mockResourcesContextValue}>
|
|
379
|
-
<Router>
|
|
380
|
-
<PatientRegistration isOffline={false} savePatientForm={mockedSavePatient} />
|
|
381
|
-
</Router>
|
|
382
|
-
</ResourcesContext.Provider>,
|
|
383
|
-
);
|
|
384
|
-
|
|
385
|
-
const givenNameInput: HTMLInputElement = screen.getByLabelText(/First Name/);
|
|
386
|
-
const familyNameInput: HTMLInputElement = screen.getByLabelText(/Family Name/);
|
|
387
|
-
const middleNameInput: HTMLInputElement = screen.getByLabelText(/Middle Name/);
|
|
388
|
-
const dateOfBirthInput: HTMLInputElement = screen.getByLabelText('Date of Birth');
|
|
389
|
-
const genderInput: HTMLInputElement = screen.getByLabelText(/Male/);
|
|
390
|
-
|
|
391
|
-
// assert initial values
|
|
392
|
-
expect(givenNameInput.value).toBe('John');
|
|
393
|
-
expect(familyNameInput.value).toBe('Wilson');
|
|
394
|
-
expect(middleNameInput.value).toBeFalsy();
|
|
395
|
-
expect(dateOfBirthInput.value).toBe('4/4/1972');
|
|
396
|
-
expect(genderInput.value).toBe('Male');
|
|
397
|
-
|
|
398
|
-
// do some edits
|
|
399
|
-
await user.clear(givenNameInput);
|
|
400
|
-
await user.clear(middleNameInput);
|
|
401
|
-
await user.clear(familyNameInput);
|
|
402
|
-
await user.type(givenNameInput, 'Eric');
|
|
403
|
-
await user.type(middleNameInput, 'Johnson');
|
|
404
|
-
await user.type(familyNameInput, 'Smith');
|
|
405
|
-
await user.click(screen.getByText('Update Patient'));
|
|
406
|
-
|
|
407
|
-
await waitFor(() =>
|
|
408
|
-
expect(mockedSavePatient).toHaveBeenCalledWith(
|
|
409
|
-
false,
|
|
410
|
-
{
|
|
411
|
-
'0': {
|
|
412
|
-
oldIdentificationNumber: '100732HE',
|
|
413
|
-
},
|
|
414
|
-
'1': {
|
|
415
|
-
openMrsId: '100GEJ',
|
|
416
|
-
},
|
|
417
|
-
addNameInLocalLanguage: undefined,
|
|
418
|
-
additionalFamilyName: '',
|
|
419
|
-
additionalGivenName: '',
|
|
420
|
-
additionalMiddleName: '',
|
|
421
|
-
address: {},
|
|
422
|
-
birthdate: new Date('1972-04-04T00:00:00.000Z'),
|
|
423
|
-
birthdateEstimated: false,
|
|
424
|
-
deathCause: '',
|
|
425
|
-
deathDate: '',
|
|
426
|
-
familyName: 'Smith',
|
|
427
|
-
gender: 'Male',
|
|
428
|
-
givenName: 'Eric',
|
|
429
|
-
identifiers: {},
|
|
430
|
-
isDead: false,
|
|
431
|
-
middleName: 'Johnson',
|
|
432
|
-
monthsEstimated: 0,
|
|
433
|
-
patientUuid: '8673ee4f-e2ab-4077-ba55-4980f408773e',
|
|
434
|
-
relationships: [],
|
|
435
|
-
telephoneNumber: '',
|
|
436
|
-
unidentifiedPatient: undefined,
|
|
437
|
-
yearsEstimated: 0,
|
|
438
|
-
},
|
|
439
|
-
expect.anything(),
|
|
440
|
-
expect.anything(),
|
|
441
|
-
null,
|
|
442
|
-
undefined,
|
|
443
|
-
expect.anything(),
|
|
444
|
-
expect.anything(),
|
|
445
|
-
expect.anything(),
|
|
446
|
-
{ patientSaved: false },
|
|
447
|
-
expect.anything(),
|
|
448
|
-
),
|
|
449
|
-
);
|
|
398
|
+
mockedUsePatient.mockReturnValue({
|
|
399
|
+
isLoading: false,
|
|
400
|
+
patient: mockPatient,
|
|
401
|
+
patientUuid: mockPatient.id,
|
|
402
|
+
error: null,
|
|
450
403
|
});
|
|
404
|
+
|
|
405
|
+
render(<PatientRegistration isOffline={false} savePatientForm={mockedSavePatient} />, { wrapper: Wrapper });
|
|
406
|
+
|
|
407
|
+
const givenNameInput: HTMLInputElement = screen.getByLabelText(/First Name/);
|
|
408
|
+
const familyNameInput: HTMLInputElement = screen.getByLabelText(/Family Name/);
|
|
409
|
+
const middleNameInput: HTMLInputElement = screen.getByLabelText(/Middle Name/);
|
|
410
|
+
const dateOfBirthInput: HTMLInputElement = screen.getByLabelText('Date of Birth');
|
|
411
|
+
const genderInput: HTMLInputElement = screen.getByLabelText(/Male/);
|
|
412
|
+
|
|
413
|
+
// assert initial values
|
|
414
|
+
expect(givenNameInput.value).toBe('John');
|
|
415
|
+
expect(familyNameInput.value).toBe('Wilson');
|
|
416
|
+
expect(middleNameInput.value).toBeFalsy();
|
|
417
|
+
expect(dateOfBirthInput.value).toBe('4/4/1972');
|
|
418
|
+
expect(genderInput.value).toBe('male');
|
|
419
|
+
|
|
420
|
+
// do some edits
|
|
421
|
+
await user.clear(givenNameInput);
|
|
422
|
+
await user.clear(middleNameInput);
|
|
423
|
+
await user.clear(familyNameInput);
|
|
424
|
+
await user.type(givenNameInput, 'Eric');
|
|
425
|
+
await user.type(middleNameInput, 'Johnson');
|
|
426
|
+
await user.type(familyNameInput, 'Smith');
|
|
427
|
+
await user.click(screen.getByText('Update Patient'));
|
|
428
|
+
|
|
429
|
+
expect(mockedSavePatient).toHaveBeenCalledWith(
|
|
430
|
+
false,
|
|
431
|
+
{
|
|
432
|
+
'0': {
|
|
433
|
+
oldIdentificationNumber: '100732HE',
|
|
434
|
+
},
|
|
435
|
+
'1': {
|
|
436
|
+
openMrsId: '100GEJ',
|
|
437
|
+
},
|
|
438
|
+
addNameInLocalLanguage: undefined,
|
|
439
|
+
additionalFamilyName: '',
|
|
440
|
+
additionalGivenName: '',
|
|
441
|
+
additionalMiddleName: '',
|
|
442
|
+
address: {},
|
|
443
|
+
birthdate: new Date('1972-04-04T00:00:00.000Z'),
|
|
444
|
+
birthdateEstimated: false,
|
|
445
|
+
deathCause: '',
|
|
446
|
+
deathDate: '',
|
|
447
|
+
familyName: 'Smith',
|
|
448
|
+
gender: expect.stringMatching(/male/i),
|
|
449
|
+
givenName: 'Eric',
|
|
450
|
+
identifiers: {},
|
|
451
|
+
isDead: false,
|
|
452
|
+
middleName: 'Johnson',
|
|
453
|
+
monthsEstimated: 0,
|
|
454
|
+
patientUuid: '8673ee4f-e2ab-4077-ba55-4980f408773e',
|
|
455
|
+
relationships: [],
|
|
456
|
+
telephoneNumber: '',
|
|
457
|
+
unidentifiedPatient: undefined,
|
|
458
|
+
yearsEstimated: 0,
|
|
459
|
+
},
|
|
460
|
+
expect.anything(),
|
|
461
|
+
expect.anything(),
|
|
462
|
+
null,
|
|
463
|
+
undefined,
|
|
464
|
+
expect.anything(),
|
|
465
|
+
expect.anything(),
|
|
466
|
+
expect.anything(),
|
|
467
|
+
{ patientSaved: false },
|
|
468
|
+
expect.anything(),
|
|
469
|
+
);
|
|
451
470
|
});
|
|
452
471
|
});
|
package/src/patient-registration/{patient-registration.types.tsx → patient-registration.types.ts}
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { OpenmrsResource, Session } from '@openmrs/esm-framework';
|
|
2
|
-
import { RegistrationConfig } from '../config-schema';
|
|
3
|
-
import { SavePatientTransactionManager } from './form-manager';
|
|
1
|
+
import { type OpenmrsResource, type Session } from '@openmrs/esm-framework';
|
|
2
|
+
import { type RegistrationConfig } from '../config-schema';
|
|
3
|
+
import { type SavePatientTransactionManager } from './form-manager';
|
|
4
4
|
|
|
5
5
|
interface NameValue {
|
|
6
6
|
uuid: string;
|
|
@@ -310,6 +310,15 @@ export interface AddressTemplate {
|
|
|
310
310
|
elementRegexFormats: ExtensibleAddressProperties;
|
|
311
311
|
requiredElements: Array<AddressProperties> | null;
|
|
312
312
|
}
|
|
313
|
+
|
|
314
|
+
// https://rest.openmrs.org/#address-template
|
|
315
|
+
export interface RestAddressTemplate {
|
|
316
|
+
uuid: string;
|
|
317
|
+
description: string;
|
|
318
|
+
property: string;
|
|
319
|
+
display: string;
|
|
320
|
+
value: string;
|
|
321
|
+
}
|
|
313
322
|
export interface ObsResponse {
|
|
314
323
|
results: Array<{ obs: Array<{ uuid: string; display: string; value: OpenmrsResource; concept: OpenmrsResource }> }>;
|
|
315
324
|
}
|