@kenyaemr/esm-patient-registration-app 4.5.3 → 4.5.5

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.
Files changed (101) hide show
  1. package/dist/117.js +2 -0
  2. package/dist/117.js.map +1 -0
  3. package/dist/130.js +1 -1
  4. package/dist/130.js.map +1 -1
  5. package/dist/208.js +1 -1
  6. package/dist/218.js +1 -0
  7. package/dist/218.js.map +1 -0
  8. package/dist/275.js +1 -0
  9. package/dist/275.js.map +1 -0
  10. package/dist/319.js +1 -1
  11. package/dist/{821.js → 348.js} +1 -1
  12. package/dist/{821.js.map → 348.js.map} +1 -1
  13. package/dist/574.js +1 -1
  14. package/dist/68.js +1 -1
  15. package/dist/68.js.map +1 -1
  16. package/dist/693.js +1 -0
  17. package/dist/693.js.map +1 -0
  18. package/dist/757.js +1 -1
  19. package/dist/788.js +1 -1
  20. package/dist/807.js +1 -1
  21. package/dist/833.js +1 -1
  22. package/dist/879.js +1 -0
  23. package/dist/879.js.map +1 -0
  24. package/dist/kenyaemr-esm-patient-registration-app.js +1 -1
  25. package/dist/kenyaemr-esm-patient-registration-app.js.buildmanifest.json +171 -122
  26. package/dist/kenyaemr-esm-patient-registration-app.js.map +1 -1
  27. package/dist/main.js +1 -1
  28. package/dist/main.js.map +1 -1
  29. package/dist/routes.json +1 -1
  30. package/jest.config.js +3 -0
  31. package/package.json +5 -2
  32. package/src/config-schema.ts +11 -5
  33. package/src/index.ts +9 -2
  34. package/src/offline.resources.ts +8 -4
  35. package/src/offline.ts +1 -1
  36. package/src/patient-registration/field/__mocks__/field.resource.ts +1 -1
  37. package/src/patient-registration/field/address/address-field.component.tsx +25 -70
  38. package/src/patient-registration/field/address/address-hierarchy-levels.component.tsx +11 -9
  39. package/src/patient-registration/field/address/address-hierarchy.resource.tsx +57 -3
  40. package/src/patient-registration/field/address/address-search.component.tsx +1 -1
  41. package/src/patient-registration/field/address/tests/address-hierarchy.test.tsx +137 -63
  42. package/src/patient-registration/field/address/tests/address-search-component.test.tsx +128 -0
  43. package/src/patient-registration/field/address/tests/mocks.ts +93 -99
  44. package/src/patient-registration/field/dob/dob.component.tsx +48 -44
  45. package/src/patient-registration/field/dob/dob.test.tsx +6 -1
  46. package/src/patient-registration/field/field.resource.ts +1 -1
  47. package/src/patient-registration/field/id/id-field.component.tsx +1 -1
  48. package/src/patient-registration/field/id/identifier-selection-overlay.tsx +1 -1
  49. package/src/patient-registration/field/name/name-field.component.tsx +38 -22
  50. package/src/patient-registration/field/obs/obs-field.component.tsx +14 -13
  51. package/src/patient-registration/field/person-attributes/coded-attributes.component.tsx +1 -1
  52. package/src/patient-registration/field/person-attributes/coded-person-attribute-field.component.tsx +1 -1
  53. package/src/patient-registration/field/person-attributes/coded-person-attribute-field.test.tsx +103 -0
  54. package/src/patient-registration/field/person-attributes/person-attribute-field.test.tsx +187 -0
  55. package/src/patient-registration/field/person-attributes/person-attributes.resource.tsx +1 -1
  56. package/src/patient-registration/field/person-attributes/text-person-attribute-field.component.tsx +3 -3
  57. package/src/patient-registration/field/person-attributes/text-person-attribute-field.test.tsx +88 -0
  58. package/src/patient-registration/form-manager.test.ts +1 -2
  59. package/src/patient-registration/form-manager.ts +1 -9
  60. package/src/patient-registration/input/basic-input/input/input.test.tsx +0 -135
  61. package/src/patient-registration/input/basic-input/select/select-input.test.tsx +8 -4
  62. package/src/patient-registration/input/combo-input/combo-input.component.tsx +8 -6
  63. package/src/patient-registration/input/custom-input/identifier/identifier-input.component.tsx +1 -1
  64. package/src/patient-registration/input/custom-input/identifier/utils.test.ts +81 -0
  65. package/src/patient-registration/input/custom-input/identifier/utils.ts +1 -1
  66. package/src/patient-registration/input/dummy-data/dummy-data-input.component.tsx +1 -2
  67. package/src/patient-registration/patient-registration-context.ts +1 -1
  68. package/src/patient-registration/patient-registration-hooks.ts +14 -2
  69. package/src/patient-registration/patient-registration-utils.ts +1 -4
  70. package/src/patient-registration/patient-registration.component.tsx +1 -12
  71. package/src/patient-registration/patient-registration.resource.tsx +1 -72
  72. package/src/patient-registration/patient-registration.test.tsx +250 -247
  73. package/src/patient-registration/{patient-registration-types.tsx → patient-registration.types.tsx} +45 -1
  74. package/src/patient-registration/section/patient-relationships/relationships-section.component.tsx +83 -79
  75. package/src/patient-registration/section/patient-relationships/relationships-section.test.tsx +88 -0
  76. package/src/patient-registration/section/patient-relationships/relationships.resource.tsx +1 -1
  77. package/src/patient-registration/validation/patient-registration-validation.tsx +1 -1
  78. package/src/patient-verification/patient-verification-hook.tsx +12 -1
  79. package/src/patient-verification/patient-verification-utils.ts +1 -1
  80. package/src/patient-verification/patient-verification.component.tsx +1 -1
  81. package/src/patient-verification/verification-modal/confirm-prompt.component.tsx +11 -0
  82. package/src/root.component.tsx +1 -1
  83. package/src/routes.json +62 -51
  84. package/src/widgets/cancel-patient-edit.test.tsx +24 -0
  85. package/src/widgets/delete-identifier-confirmation-modal.test.tsx +31 -0
  86. package/src/widgets/display-photo.test.tsx +37 -0
  87. package/src/widgets/edit-patient-details-button.test.tsx +36 -0
  88. package/translations/am.json +0 -7
  89. package/translations/en.json +2 -5
  90. package/translations/es.json +0 -7
  91. package/translations/fr.json +0 -7
  92. package/translations/he.json +0 -7
  93. package/translations/km.json +0 -7
  94. package/__mocks__/react-i18next.js +0 -49
  95. package/dist/196.js +0 -1
  96. package/dist/196.js.map +0 -1
  97. package/dist/59.js +0 -1
  98. package/dist/59.js.map +0 -1
  99. package/dist/9.js +0 -2
  100. package/dist/9.js.map +0 -1
  101. /package/dist/{9.js.LICENSE.txt → 117.js.LICENSE.txt} +0 -0
@@ -1,15 +1,16 @@
1
1
  import React from 'react';
2
- import { BrowserRouter as Router } from 'react-router-dom';
3
- import { render, screen, fireEvent, waitFor, within } from '@testing-library/react';
2
+ import { BrowserRouter as Router, useParams } from 'react-router-dom';
3
+ import { render, screen, waitFor, within } from '@testing-library/react';
4
4
  import userEvent from '@testing-library/user-event';
5
5
  import { showToast, useConfig, usePatient } from '@openmrs/esm-framework';
6
- import FormManager from './form-manager';
7
- import { mockPatient } from '../../../../__mocks__/patient.mock';
6
+ import { FormManager } from './form-manager';
8
7
  import { saveEncounter, savePatient } from './patient-registration.resource';
9
8
  import { Encounter } from './patient-registration-types';
10
9
  import { Resources, ResourcesContext } from '../offline.resources';
11
10
  import { PatientRegistration } from './patient-registration.component';
12
11
  import { RegistrationConfig } from '../config-schema';
12
+ import { mockedAddressTemplate } from './field/address/tests/mocks';
13
+ import { mockPatient } from '../../../../tools/test-helpers';
13
14
 
14
15
  const mockedUseConfig = useConfig as jest.Mock;
15
16
  const mockedUsePatient = usePatient as jest.Mock;
@@ -35,6 +36,7 @@ jest.mock('react-router-dom', () => ({
35
36
  pathname: 'openmrs/spa/patient-registration',
36
37
  }),
37
38
  useHistory: () => [],
39
+ useParams: jest.fn().mockReturnValue({ patientUuid: undefined }),
38
40
  }));
39
41
 
40
42
  jest.mock('./patient-registration.resource', () => {
@@ -56,17 +58,8 @@ jest.mock('@openmrs/esm-framework', () => {
56
58
  };
57
59
  });
58
60
 
59
- const predefinedAddressTemplate = {
60
- results: [
61
- {
62
- value:
63
- '<org.openmrs.layout.address.AddressTemplate>\r\n <nameMappings class="properties">\r\n <property name="postalCode" value="Location.postalCode"/>\r\n <property name="address2" value="Location.address2"/>\r\n <property name="address1" value="Location.address1"/>\r\n <property name="country" value="Location.country"/>\r\n <property name="stateProvince" value="Location.stateProvince"/>\r\n <property name="cityVillage" value="Location.cityVillage"/>\r\n </nameMappings>\r\n <sizeMappings class="properties">\r\n <property name="postalCode" value="4"/>\r\n <property name="address1" value="40"/>\r\n <property name="address2" value="40"/>\r\n <property name="country" value="10"/>\r\n <property name="stateProvince" value="10"/>\r\n <property name="cityVillage" value="10"/>\r\n <asset name="cityVillage" value="10"/>\r\n </sizeMappings>\r\n <lineByLineFormat>\r\n <string>address1 address2</string>\r\n <string>cityVillage stateProvince postalCode</string>\r\n <string>country</string>\r\n </lineByLineFormat>\r\n <elementDefaults class="properties">\r\n <property name="country" value=""/>\r\n </elementDefaults>\r\n <elementRegex class="properties">\r\n <property name="address1" value="[a-zA-Z]+$"/>\r\n </elementRegex>\r\n <elementRegexFormats class="properties">\r\n <property name="address1" value="Countries can only be letters"/>\r\n </elementRegexFormats>\r\n </org.openmrs.layout.address.AddressTemplate>',
64
- },
65
- ],
66
- };
67
-
68
61
  const mockResourcesContextValue = {
69
- addressTemplate: predefinedAddressTemplate,
62
+ addressTemplate: mockedAddressTemplate,
70
63
  currentSession: {
71
64
  authenticated: true,
72
65
  sessionId: 'JSESSION',
@@ -87,9 +80,11 @@ let mockOpenmrsConfig: RegistrationConfig = {
87
80
  fieldConfigurations: {
88
81
  name: {
89
82
  displayMiddleName: true,
90
- unidentifiedPatient: true,
83
+ allowUnidentifiedPatients: true,
91
84
  defaultUnknownGivenName: 'UNKNOWN',
92
85
  defaultUnknownFamilyName: 'UNKNOWN',
86
+ displayReverseFieldOrder: false,
87
+ displayCapturePhoto: true,
93
88
  },
94
89
  gender: [
95
90
  {
@@ -173,264 +168,272 @@ const fillRequiredFields = async () => {
173
168
  await user.type(familyNameInput, 'Gaihre');
174
169
  await user.clear(dateOfBirthInput);
175
170
  await user.type(dateOfBirthInput, '02/08/1993');
176
- fireEvent.blur(dateOfBirthInput);
177
- fireEvent.click(genderInput);
171
+ user.click(genderInput);
178
172
  };
179
173
 
180
- describe('patient registration', () => {
181
- beforeEach(() => {
182
- mockedUseConfig.mockReturnValue(mockOpenmrsConfig);
183
- mockedSavePatient.mockReturnValue({ data: { uuid: 'new-pt-uuid' }, ok: true });
184
- mockedSaveEncounter.mockClear();
185
- mockedShowToast.mockClear();
186
- });
174
+ describe('patient registration component', () => {
175
+ describe('when registering a new patient', () => {
176
+ beforeEach(() => {
177
+ mockedUseConfig.mockReturnValue(mockOpenmrsConfig);
178
+ mockedSavePatient.mockReturnValue({ data: { uuid: 'new-pt-uuid' }, ok: true });
179
+ mockedSaveEncounter.mockClear();
180
+ mockedShowToast.mockClear();
181
+ jest.clearAllMocks();
182
+ });
187
183
 
188
- it.only('renders without crashing', () => {
189
- render(
190
- <ResourcesContext.Provider value={mockResourcesContextValue}>
191
- <Router>
192
- <PatientRegistration isOffline={false} savePatientForm={jest.fn()} />
193
- </Router>
194
- ,
195
- </ResourcesContext.Provider>,
196
- );
197
- });
184
+ it('renders without crashing', () => {
185
+ render(
186
+ <ResourcesContext.Provider value={mockResourcesContextValue}>
187
+ <Router>
188
+ <PatientRegistration isOffline={false} savePatientForm={jest.fn()} />
189
+ </Router>
190
+ ,
191
+ </ResourcesContext.Provider>,
192
+ );
193
+ });
198
194
 
199
- it('has the expected sections', async () => {
200
- render(
201
- <ResourcesContext.Provider value={mockResourcesContextValue}>
202
- <Router>
203
- <PatientRegistration isOffline={false} savePatientForm={jest.fn()} />
204
- </Router>
205
- </ResourcesContext.Provider>,
206
- );
207
- await waitFor(() => expect(screen.getByLabelText(/Demographics Section/)).not.toBeNull());
208
- expect(screen.getByLabelText(/Contact Info Section/)).not.toBeNull();
209
- });
195
+ it('has the expected sections', async () => {
196
+ render(
197
+ <ResourcesContext.Provider value={mockResourcesContextValue}>
198
+ <Router>
199
+ <PatientRegistration isOffline={false} savePatientForm={jest.fn()} />
200
+ </Router>
201
+ </ResourcesContext.Provider>,
202
+ );
203
+ await waitFor(() => expect(screen.getByLabelText(/Demographics Section/)).not.toBeNull());
204
+ expect(screen.getByLabelText(/Contact Info Section/)).not.toBeNull();
205
+ });
210
206
 
211
- it('saves the patient without extra info', async () => {
212
- const user = userEvent.setup();
213
-
214
- render(
215
- <ResourcesContext.Provider value={mockResourcesContextValue}>
216
- <Router>
217
- <PatientRegistration isOffline={false} savePatientForm={FormManager.savePatientFormOnline} />
218
- </Router>
219
- </ResourcesContext.Provider>,
220
- );
221
-
222
- await fillRequiredFields();
223
- await user.click(await screen.findByText('Register Patient'));
224
- await waitFor(() => {
225
- expect(mockedSavePatient).toHaveBeenCalledWith(
226
- expect.anything(),
227
- expect.objectContaining({
228
- identifiers: [], //TODO when the identifer story is finished: { identifier: '', identifierType: '05a29f94-c0ed-11e2-94be-8c13b969e334', location: '' }
229
- // identifiers: [{ identifier: '', identifierType: '05a29f94-c0ed-11e2-94be-8c13b969e334', location: '' }],
230
- person: {
231
- addresses: [{}],
232
- attributes: [],
233
- birthdate: '1993-8-2',
234
- birthdateEstimated: false,
235
- gender: 'M',
236
- names: [{ givenName: 'Paul', middleName: '', familyName: 'Gaihre', preferred: true, uuid: undefined }],
237
- dead: false,
238
- uuid: expect.anything(),
239
- },
240
- uuid: expect.anything(),
241
- }),
242
- undefined,
207
+ it('saves the patient without extra info', async () => {
208
+ const user = userEvent.setup();
209
+
210
+ render(
211
+ <ResourcesContext.Provider value={mockResourcesContextValue}>
212
+ <Router>
213
+ <PatientRegistration isOffline={false} savePatientForm={FormManager.savePatientFormOnline} />
214
+ </Router>
215
+ </ResourcesContext.Provider>,
243
216
  );
217
+
218
+ await fillRequiredFields();
219
+ await user.click(await screen.findByText('Register Patient'));
220
+ await waitFor(() => {
221
+ expect(mockedSavePatient).toHaveBeenCalledWith(
222
+ expect.objectContaining({
223
+ identifiers: [], //TODO when the identifer story is finished: { identifier: '', identifierType: '05a29f94-c0ed-11e2-94be-8c13b969e334', location: '' },
224
+ person: {
225
+ addresses: expect.arrayContaining([expect.any(Object)]),
226
+ attributes: [],
227
+ birthdate: '1993-8-2',
228
+ birthdateEstimated: false,
229
+ gender: 'M',
230
+ names: [{ givenName: 'Paul', middleName: '', familyName: 'Gaihre', preferred: true, uuid: undefined }],
231
+ dead: false,
232
+ uuid: expect.anything(),
233
+ },
234
+ uuid: expect.anything(),
235
+ }),
236
+ undefined,
237
+ );
238
+ });
244
239
  });
245
- });
246
240
 
247
- it('should not save the patient if validation fails', async () => {
248
- const user = userEvent.setup();
241
+ it('should not save the patient if validation fails', async () => {
242
+ const user = userEvent.setup();
243
+
244
+ const mockedSavePatientForm = jest.fn();
245
+ render(
246
+ <ResourcesContext.Provider value={mockResourcesContextValue}>
247
+ <Router>
248
+ <PatientRegistration isOffline={false} savePatientForm={mockedSavePatientForm} />
249
+ </Router>
250
+ </ResourcesContext.Provider>,
251
+ );
249
252
 
250
- const mockedSavePatientForm = jest.fn();
251
- render(
252
- <ResourcesContext.Provider value={mockResourcesContextValue}>
253
- <Router>
254
- <PatientRegistration isOffline={false} savePatientForm={mockedSavePatientForm} />
255
- </Router>
256
- </ResourcesContext.Provider>,
257
- );
253
+ const givenNameInput = (await screen.findByLabelText('First Name')) as HTMLInputElement;
258
254
 
259
- const givenNameInput = (await screen.findByLabelText('First Name')) as HTMLInputElement;
255
+ await user.type(givenNameInput, '5');
256
+ await user.click(screen.getByText('Register Patient'));
260
257
 
261
- await user.type(givenNameInput, '');
262
- await user.click(screen.getByText('Register Patient'));
258
+ expect(mockedSavePatientForm).not.toHaveBeenCalled();
259
+ });
263
260
 
264
- expect(mockedSavePatientForm).not.toHaveBeenCalled();
265
- });
261
+ it('renders and saves registration obs', async () => {
262
+ const user = userEvent.setup();
266
263
 
267
- it.skip('edits patient demographics', async () => {
268
- const user = userEvent.setup();
264
+ mockedSaveEncounter.mockResolvedValue({});
265
+ mockedUseConfig.mockReturnValue(configWithObs);
269
266
 
270
- mockedSavePatient.mockResolvedValue({});
267
+ render(
268
+ <ResourcesContext.Provider value={mockResourcesContextValue}>
269
+ <Router>
270
+ <PatientRegistration isOffline={false} savePatientForm={FormManager.savePatientFormOnline} />
271
+ </Router>
272
+ </ResourcesContext.Provider>,
273
+ );
271
274
 
272
- mockedUsePatient.mockReturnValueOnce({
273
- isLoading: false,
274
- patient: mockPatient,
275
- patientUuid: mockPatient.id,
276
- error: null,
275
+ await fillRequiredFields();
276
+ const customSection = screen.getByLabelText('Custom Section');
277
+ const weight = within(customSection).getByLabelText('Weight (kg) (optional)');
278
+ await user.type(weight, '50');
279
+ const complaint = within(customSection).getByLabelText('Chief Complaint (optional)');
280
+ await user.type(complaint, 'sad');
281
+ const nationality = within(customSection).getByLabelText('Nationality');
282
+ await user.selectOptions(nationality, 'USA');
283
+
284
+ await user.click(screen.getByText('Register Patient'));
285
+
286
+ await waitFor(() => expect(mockedSavePatient).toHaveBeenCalled());
287
+ await waitFor(() =>
288
+ expect(mockedSaveEncounter).toHaveBeenCalledWith(
289
+ expect.objectContaining<Partial<Encounter>>({
290
+ encounterType: 'reg-enc-uuid',
291
+ patient: 'new-pt-uuid',
292
+ obs: [
293
+ { concept: 'weight-uuid', value: 50 },
294
+ { concept: 'chief-complaint-uuid', value: 'sad' },
295
+ { concept: 'nationality-uuid', value: 'usa' },
296
+ ],
297
+ }),
298
+ ),
299
+ );
277
300
  });
278
301
 
279
- render(
280
- <ResourcesContext.Provider value={mockResourcesContextValue}>
281
- <Router>
282
- <PatientRegistration isOffline={false} savePatientForm={FormManager.savePatientFormOnline} />
283
- </Router>
284
- </ResourcesContext.Provider>,
285
- );
286
-
287
- const givenNameInput = screen.getByLabelText(/First Name/) as HTMLInputElement;
288
- const familyNameInput = screen.getByLabelText(/Family Name/) as HTMLInputElement;
289
- const middleNameInput = screen.getByLabelText(/Middle Name/) as HTMLInputElement;
290
- const dateOfBirthInput = screen.getByLabelText('Date of Birth') as HTMLInputElement;
291
- const address1 = screen.getByLabelText('Location.address1') as HTMLInputElement;
292
-
293
- // assert initial values
294
- expect(givenNameInput.value).toBe('John');
295
- expect(familyNameInput.value).toBe('Wilson');
296
- expect(middleNameInput.value).toBeFalsy();
297
- expect(dateOfBirthInput.value).toBe('04/04/1972');
298
-
299
- // do some edits
300
- await user.clear(givenNameInput);
301
- await user.clear(middleNameInput);
302
- await user.clear(familyNameInput);
303
- await user.type(givenNameInput, 'Eric');
304
- await user.type(middleNameInput, 'Johnson');
305
- await user.type(familyNameInput, 'Smith');
306
- await user.type(address1, 'Bom Jesus Street');
307
- await user.click(screen.getByText('Update Patient'));
308
-
309
- expect(mockedSavePatient).toHaveBeenCalledWith(
310
- expect.anything(),
311
- {
312
- uuid: '8673ee4f-e2ab-4077-ba55-4980f408773e',
313
- identifiers: [
314
- {
315
- uuid: '1f0ad7a1-430f-4397-b571-59ea654a52db',
316
- identifier: '100GEJ',
317
- identifierType: 'e5af9a9c-ff9d-486d-900c-5fbf66a5ba3c',
318
- preferred: true,
319
- },
320
- {
321
- uuid: '1f0ad7a1-430f-4397-b571-59ea654a52db',
322
- identifier: '100732HE',
323
- identifierType: '3ff0063c-dd45-4d98-8af4-0c094f26166c',
324
- preferred: false,
325
- },
326
- ],
327
- person: {
328
- addresses: [
329
- {
330
- address1: 'Bom Jesus Street',
331
- address2: '',
332
- cityVillage: 'City0351',
333
- country: 'Country0351',
334
- postalCode: '60351',
335
- stateProvince: 'State0351tested',
336
- },
337
- ],
338
- attributes: [],
339
- birthdate: new Date('1972-04-04'),
340
- birthdateEstimated: false,
341
- gender: 'M',
342
- names: [
343
- {
344
- uuid: 'efdb246f-4142-4c12-a27a-9be60b9592e9',
345
- givenName: 'Eric',
346
- middleName: 'Johnson',
347
- familyName: 'Smith',
348
- preferred: true,
349
- },
350
- ],
351
- dead: false,
352
- uuid: '8673ee4f-e2ab-4077-ba55-4980f408773e',
353
- },
354
- },
355
- '8673ee4f-e2ab-4077-ba55-4980f408773e',
356
- );
357
- });
302
+ it('retries saving registration obs after a failed attempt', async () => {
303
+ const user = userEvent.setup();
358
304
 
359
- it('renders and saves registration obs', async () => {
360
- const user = userEvent.setup();
361
-
362
- mockedSaveEncounter.mockResolvedValue({});
363
- mockedUseConfig.mockReturnValue(configWithObs);
364
-
365
- render(
366
- <ResourcesContext.Provider value={mockResourcesContextValue}>
367
- <Router>
368
- <PatientRegistration isOffline={false} savePatientForm={FormManager.savePatientFormOnline} />
369
- </Router>
370
- </ResourcesContext.Provider>,
371
- );
372
-
373
- await fillRequiredFields();
374
- const customSection = screen.getByLabelText('Custom Section');
375
- const weight = within(customSection).getByLabelText('Weight (kg)');
376
- await user.type(weight, '50');
377
- const complaint = within(customSection).getByLabelText('Chief Complaint');
378
- await user.type(complaint, 'sad');
379
- const nationality = within(customSection).getByLabelText('Nationality');
380
- await user.selectOptions(nationality, 'USA');
381
-
382
- await user.click(screen.getByText('Register Patient'));
383
-
384
- await waitFor(() => expect(mockedSavePatient).toHaveBeenCalled());
385
- await waitFor(() =>
386
- expect(mockedSaveEncounter).toHaveBeenCalledWith(
387
- expect.anything(),
388
- expect.objectContaining<Partial<Encounter>>({
389
- encounterType: 'reg-enc-uuid',
390
- patient: 'new-pt-uuid',
391
- obs: [
392
- { concept: 'weight-uuid', value: 50 },
393
- { concept: 'chief-complaint-uuid', value: 'sad' },
394
- { concept: 'nationality-uuid', value: 'usa' },
395
- ],
396
- }),
397
- ),
398
- );
399
- });
305
+ mockedUseConfig.mockReturnValue(configWithObs);
400
306
 
401
- it('retries saving registration obs after a failed attempt', async () => {
402
- const user = userEvent.setup();
307
+ render(
308
+ <ResourcesContext.Provider value={mockResourcesContextValue}>
309
+ <Router>
310
+ <PatientRegistration isOffline={false} savePatientForm={FormManager.savePatientFormOnline} />
311
+ </Router>
312
+ </ResourcesContext.Provider>,
313
+ );
403
314
 
404
- mockedUseConfig.mockReturnValue(configWithObs);
315
+ await fillRequiredFields();
316
+ const customSection = screen.getByLabelText('Custom Section');
317
+ const weight = within(customSection).getByLabelText('Weight (kg) (optional)');
318
+ await user.type(weight, '-999');
405
319
 
406
- render(
407
- <ResourcesContext.Provider value={mockResourcesContextValue}>
408
- <Router>
409
- <PatientRegistration isOffline={false} savePatientForm={FormManager.savePatientFormOnline} />
410
- </Router>
411
- </ResourcesContext.Provider>,
412
- );
320
+ mockedSaveEncounter.mockRejectedValue({ status: 400, responseBody: { error: { message: 'an error message' } } });
413
321
 
414
- await fillRequiredFields();
415
- const customSection = screen.getByLabelText('Custom Section');
416
- const weight = within(customSection).getByLabelText('Weight (kg)');
417
- await user.type(weight, '-999');
322
+ await user.click(screen.getByText('Register Patient'));
418
323
 
419
- mockedSaveEncounter.mockRejectedValue({ status: 400, responseBody: { error: { message: 'an error message' } } });
324
+ await waitFor(() => expect(mockedSavePatient).toHaveBeenCalledTimes(1));
325
+ await waitFor(() => expect(mockedSaveEncounter).toHaveBeenCalledTimes(1));
326
+ await waitFor(() =>
327
+ expect(mockedShowToast).toHaveBeenCalledWith(expect.objectContaining({ description: 'an error message' })),
328
+ );
420
329
 
421
- await user.click(screen.getByText('Register Patient'));
330
+ mockedSaveEncounter.mockResolvedValue({});
422
331
 
423
- await waitFor(() => expect(mockedSavePatient).toHaveBeenCalledTimes(1));
424
- await waitFor(() => expect(mockedSaveEncounter).toHaveBeenCalledTimes(1));
425
- await waitFor(() =>
426
- expect(mockedShowToast).toHaveBeenCalledWith(expect.objectContaining({ description: 'an error message' })),
427
- );
332
+ await user.click(screen.getByText('Register Patient'));
333
+ await waitFor(() => expect(mockedSavePatient).toHaveBeenCalledTimes(2));
334
+ await waitFor(() => expect(mockedSaveEncounter).toHaveBeenCalledTimes(2));
335
+ await waitFor(() => expect(mockedShowToast).toHaveBeenCalledWith(expect.objectContaining({ kind: 'success' })));
336
+ });
337
+ });
338
+
339
+ describe('when updating an existing patient details', () => {
340
+ beforeEach(() => {
341
+ mockedUseConfig.mockReturnValue(mockOpenmrsConfig);
342
+ mockedSavePatient.mockReturnValue({ data: { uuid: 'new-pt-uuid' }, ok: true });
343
+ mockedSaveEncounter.mockClear();
344
+ mockedShowToast.mockClear();
345
+ jest.clearAllMocks();
346
+ });
428
347
 
429
- mockedSaveEncounter.mockResolvedValue({});
348
+ it('edits patient demographics', async () => {
349
+ const user = userEvent.setup();
430
350
 
431
- await user.click(screen.getByText('Register Patient'));
432
- await waitFor(() => expect(mockedSavePatient).toHaveBeenCalledTimes(1));
433
- await waitFor(() => expect(mockedSaveEncounter).toHaveBeenCalledTimes(2));
434
- await waitFor(() => expect(mockedShowToast).toHaveBeenCalledWith(expect.objectContaining({ kind: 'success' })));
351
+ mockedSavePatient.mockResolvedValue({});
352
+
353
+ const mockedUseParams = useParams as jest.Mock;
354
+
355
+ mockedUseParams.mockReturnValue({ patientUuid: mockPatient.id });
356
+
357
+ mockedUsePatient.mockReturnValue({
358
+ isLoading: false,
359
+ patient: mockPatient,
360
+ patientUuid: mockPatient.id,
361
+ error: null,
362
+ });
363
+
364
+ render(
365
+ <ResourcesContext.Provider value={mockResourcesContextValue}>
366
+ <Router>
367
+ <PatientRegistration isOffline={false} savePatientForm={mockedSavePatient} />
368
+ </Router>
369
+ </ResourcesContext.Provider>,
370
+ );
371
+
372
+ const givenNameInput: HTMLInputElement = screen.getByLabelText(/First Name/);
373
+ const familyNameInput: HTMLInputElement = screen.getByLabelText(/Family Name/);
374
+ const middleNameInput: HTMLInputElement = screen.getByLabelText(/Middle Name/);
375
+ const dateOfBirthInput: HTMLInputElement = screen.getByLabelText('Date of Birth');
376
+ const genderInput: HTMLInputElement = screen.getByLabelText(/Male/);
377
+
378
+ // assert initial values
379
+ expect(givenNameInput.value).toBe('John');
380
+ expect(familyNameInput.value).toBe('Wilson');
381
+ expect(middleNameInput.value).toBeFalsy();
382
+ expect(dateOfBirthInput.value).toBe('4/4/1972');
383
+ expect(genderInput.value).toBe('Male');
384
+
385
+ // do some edits
386
+ await user.clear(givenNameInput);
387
+ await user.clear(middleNameInput);
388
+ await user.clear(familyNameInput);
389
+ await user.type(givenNameInput, 'Eric');
390
+ await user.type(middleNameInput, 'Johnson');
391
+ await user.type(familyNameInput, 'Smith');
392
+ await user.click(screen.getByText('Update Patient'));
393
+
394
+ await waitFor(() =>
395
+ expect(mockedSavePatient).toHaveBeenCalledWith(
396
+ false,
397
+ {
398
+ '0': {
399
+ oldIdentificationNumber: '100732HE',
400
+ },
401
+ '1': {
402
+ openMrsId: '100GEJ',
403
+ },
404
+ addNameInLocalLanguage: undefined,
405
+ additionalFamilyName: '',
406
+ additionalGivenName: '',
407
+ additionalMiddleName: '',
408
+ address: {},
409
+ birthdate: new Date('1972-04-04T00:00:00.000Z'),
410
+ birthdateEstimated: false,
411
+ deathCause: '',
412
+ deathDate: '',
413
+ familyName: 'Smith',
414
+ gender: 'Male',
415
+ givenName: 'Eric',
416
+ identifiers: {},
417
+ isDead: false,
418
+ middleName: 'Johnson',
419
+ monthsEstimated: 0,
420
+ patientUuid: '8673ee4f-e2ab-4077-ba55-4980f408773e',
421
+ relationships: [],
422
+ telephoneNumber: '',
423
+ unidentifiedPatient: undefined,
424
+ yearsEstimated: 0,
425
+ },
426
+ expect.anything(),
427
+ expect.anything(),
428
+ null,
429
+ undefined,
430
+ expect.anything(),
431
+ expect.anything(),
432
+ expect.anything(),
433
+ { patientSaved: false },
434
+ expect.anything(),
435
+ ),
436
+ );
437
+ });
435
438
  });
436
439
  });
@@ -158,7 +158,6 @@ export interface FormValues {
158
158
  givenName: string;
159
159
  middleName: string;
160
160
  familyName: string;
161
- unidentifiedPatient: boolean;
162
161
  additionalGivenName: string;
163
162
  additionalMiddleName: string;
164
163
  additionalFamilyName: string;
@@ -266,6 +265,51 @@ export interface ConceptAnswers {
266
265
  display: string;
267
266
  uuid: string;
268
267
  }
268
+
269
+ export type AddressProperties =
270
+ | 'cityVillage'
271
+ | 'stateProvince'
272
+ | 'countyDistrict'
273
+ | 'postalCode'
274
+ | 'country'
275
+ | 'address1'
276
+ | 'address2'
277
+ | 'address3'
278
+ | 'address4'
279
+ | 'address5'
280
+ | 'address6'
281
+ | 'address7'
282
+ | 'address8'
283
+ | 'address9'
284
+ | 'address10'
285
+ | 'address11'
286
+ | 'address12'
287
+ | 'address13'
288
+ | 'address14'
289
+ | 'address15';
290
+
291
+ export type ExtensibleAddressProperties = { [p in AddressProperties]?: string } | null;
292
+
293
+ export interface AddressTemplate {
294
+ displayName: string | null;
295
+ codeName: string | null;
296
+ country: string | null;
297
+ lines: Array<
298
+ Array<{
299
+ isToken: 'IS_NOT_ADDR_TOKEN' | 'IS_ADDR_TOKEN';
300
+ displayText: string;
301
+ codeName?: AddressProperties;
302
+ displaySize?: string;
303
+ }>
304
+ > | null;
305
+ lineByLineFormat: Array<string> | null;
306
+ nameMappings: ExtensibleAddressProperties;
307
+ sizeMappings: ExtensibleAddressProperties;
308
+ elementDefaults: ExtensibleAddressProperties;
309
+ elementRegex: ExtensibleAddressProperties;
310
+ elementRegexFormats: ExtensibleAddressProperties;
311
+ requiredElements: Array<AddressProperties> | null;
312
+ }
269
313
  export interface ObsResponse {
270
314
  results: Array<{ obs: Array<{ uuid: string; display: string; value: OpenmrsResource; concept: OpenmrsResource }> }>;
271
315
  }