@kenyaemr/esm-patient-registration-app 8.1.1-pre.129 → 8.1.2-pre.154

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 (124) hide show
  1. package/.turbo/turbo-build.log +21 -23
  2. package/dist/108.js +1 -1
  3. package/dist/130.js +1 -1
  4. package/dist/130.js.map +1 -1
  5. package/dist/173.js +2 -0
  6. package/dist/{895.js.LICENSE.txt → 173.js.LICENSE.txt} +25 -0
  7. package/dist/173.js.map +1 -0
  8. package/dist/236.js +1 -0
  9. package/dist/240.js +1 -0
  10. package/dist/261.js +1 -0
  11. package/dist/271.js +1 -1
  12. package/dist/272.js +1 -0
  13. package/dist/319.js +1 -1
  14. package/dist/336.js +1 -0
  15. package/dist/371.js +1 -0
  16. package/dist/371.js.map +1 -0
  17. package/dist/378.js +1 -0
  18. package/dist/460.js +1 -1
  19. package/dist/501.js +1 -1
  20. package/dist/501.js.map +1 -1
  21. package/dist/539.js +1 -0
  22. package/dist/566.js +1 -0
  23. package/dist/574.js +1 -1
  24. package/dist/623.js +1 -0
  25. package/dist/623.js.map +1 -0
  26. package/dist/644.js +1 -1
  27. package/dist/652.js +1 -0
  28. package/dist/657.js +1 -0
  29. package/dist/657.js.map +1 -0
  30. package/dist/673.js +1 -0
  31. package/dist/705.js +1 -0
  32. package/dist/711.js +1 -0
  33. package/dist/727.js +1 -0
  34. package/dist/737.js +1 -0
  35. package/dist/744.js +1 -0
  36. package/dist/757.js +1 -1
  37. package/dist/759.js +1 -0
  38. package/dist/759.js.map +1 -0
  39. package/dist/76.js +1 -1
  40. package/dist/788.js +1 -1
  41. package/dist/807.js +1 -1
  42. package/dist/833.js +1 -1
  43. package/dist/899.js +1 -0
  44. package/dist/kenyaemr-esm-patient-registration-app.js +1 -1
  45. package/dist/kenyaemr-esm-patient-registration-app.js.buildmanifest.json +445 -93
  46. package/dist/kenyaemr-esm-patient-registration-app.js.map +1 -1
  47. package/dist/main.js +1 -1
  48. package/dist/main.js.LICENSE.txt +25 -0
  49. package/dist/main.js.map +1 -1
  50. package/dist/routes.json +1 -1
  51. package/package-lock.json +2052 -1699
  52. package/package.json +4 -4
  53. package/src/client-registry/hie-client-registry/dependants/dependants.component.tsx +46 -0
  54. package/src/client-registry/hie-client-registry/hie-client-registry.component.tsx +38 -8
  55. package/src/client-registry/hie-client-registry/hie-resource.ts +126 -21
  56. package/src/client-registry/hie-client-registry/hie-types.ts +102 -0
  57. package/src/client-registry/hie-client-registry/modal/confirm-hie.modal.tsx +78 -62
  58. package/src/client-registry/hie-client-registry/modal/confirm-hie.scss +55 -3
  59. package/src/client-registry/hie-client-registry/modal/hie-otp-verification-form.component.tsx +88 -0
  60. package/src/client-registry/hie-client-registry/modal/hie-patient-detail-preview.component.tsx +77 -0
  61. package/src/client-registry/hie-client-registry/patient-info/patient-info.component.tsx +17 -0
  62. package/src/config-schema.ts +30 -2
  63. package/src/patient-registration/field/address/address-search.component.tsx +5 -2
  64. package/src/patient-registration/field/date-and-time-of-death/date-and-time-of-death.component.tsx +1 -1
  65. package/src/patient-registration/field/dob/dob.component.tsx +1 -1
  66. package/src/patient-registration/field/gender/gender-field.component.tsx +6 -2
  67. package/src/patient-registration/field/id/identifier-selection-overlay.component.tsx +3 -3
  68. package/src/patient-registration/field/name/name-field.component.tsx +2 -2
  69. package/src/patient-registration/field/obs/obs-field.component.tsx +9 -5
  70. package/src/patient-registration/field/person-attributes/coded-person-attribute-field.component.tsx +3 -3
  71. package/src/patient-registration/field/person-attributes/coded-person-attribute-field.test.tsx +22 -11
  72. package/src/patient-registration/field/person-attributes/location-person-attribute-field.component.tsx +1 -1
  73. package/src/patient-registration/field/person-attributes/person-attribute-field.test.tsx +12 -4
  74. package/src/patient-registration/form-manager.test.ts +4 -1
  75. package/src/patient-registration/form-manager.ts +0 -1
  76. package/src/patient-registration/input/custom-input/autosuggest/autosuggest.test.tsx +52 -62
  77. package/src/patient-registration/mpi/mpi-patient.resource.ts +21 -0
  78. package/src/patient-registration/patient-registration-hooks.ts +90 -25
  79. package/src/patient-registration/patient-registration-utils.test.ts +33 -0
  80. package/src/patient-registration/patient-registration-utils.ts +63 -13
  81. package/src/patient-registration/patient-registration.component.tsx +17 -2
  82. package/src/patient-registration/patient-registration.test.tsx +442 -56
  83. package/src/patient-registration/section/demographics/demographics-section.component.tsx +3 -3
  84. package/src/patient-registration/section/patient-relationships/relationships-section.component.tsx +1 -1
  85. package/src/patient-registration/section/patient-relationships/relationships.resource.tsx +28 -28
  86. package/src/widgets/cancel-patient-edit.modal.tsx +2 -0
  87. package/src/widgets/cancel-patient-edit.scss +29 -0
  88. package/src/widgets/delete-identifier-confirmation.modal.tsx +2 -0
  89. package/src/widgets/delete-identifier-confirmation.scss +29 -0
  90. package/translations/am.json +1 -0
  91. package/translations/ar.json +6 -4
  92. package/translations/de.json +118 -0
  93. package/translations/en.json +17 -0
  94. package/translations/es.json +2 -0
  95. package/translations/fr.json +1 -0
  96. package/translations/he.json +1 -0
  97. package/translations/hi.json +118 -0
  98. package/translations/hi_IN.json +118 -0
  99. package/translations/id.json +118 -0
  100. package/translations/it.json +118 -0
  101. package/translations/km.json +1 -0
  102. package/translations/ne.json +118 -0
  103. package/translations/pt.json +118 -0
  104. package/translations/pt_BR.json +118 -0
  105. package/translations/qu.json +118 -0
  106. package/translations/si.json +118 -0
  107. package/translations/sw.json +118 -0
  108. package/translations/sw_KE.json +118 -0
  109. package/translations/tr.json +118 -0
  110. package/translations/tr_TR.json +118 -0
  111. package/translations/uk.json +118 -0
  112. package/translations/vi.json +118 -0
  113. package/translations/zh.json +3 -1
  114. package/translations/zh_CN.json +2 -0
  115. package/dist/250.js +0 -1
  116. package/dist/250.js.map +0 -1
  117. package/dist/66.js +0 -1
  118. package/dist/66.js.map +0 -1
  119. package/dist/662.js +0 -1
  120. package/dist/662.js.map +0 -1
  121. package/dist/753.js +0 -1
  122. package/dist/753.js.map +0 -1
  123. package/dist/895.js +0 -2
  124. package/dist/895.js.map +0 -1
@@ -1,8 +1,8 @@
1
1
  import React from 'react';
2
2
  import dayjs from 'dayjs';
3
3
  import userEvent from '@testing-library/user-event';
4
- import { BrowserRouter as Router, useParams } from 'react-router-dom';
5
- import { render, screen, within } from '@testing-library/react';
4
+ import { BrowserRouter as Router, useParams, useLocation } from 'react-router-dom';
5
+ import { act, render, screen, within } from '@testing-library/react';
6
6
  import {
7
7
  type FetchResponse,
8
8
  getDefaultsFromConfigSchema,
@@ -10,22 +10,32 @@ import {
10
10
  showSnackbar,
11
11
  useConfig,
12
12
  usePatient,
13
+ openmrsFetch,
13
14
  } from '@openmrs/esm-framework';
15
+ import type { AddressTemplate, Encounter, FormValues } from './patient-registration.types';
14
16
  import { mockedAddressTemplate } from '__mocks__';
15
- import { mockPatient } from 'tools';
17
+ import { mockPatient, mockOpenMRSIdentificationNumberIdType } from 'tools';
16
18
  import { saveEncounter, savePatient } from './patient-registration.resource';
17
19
  import { esmPatientRegistrationSchema, type RegistrationConfig } from '../config-schema';
18
- import type { AddressTemplate, Encounter } from './patient-registration.types';
19
20
  import { ResourcesContext } from '../offline.resources';
20
21
  import { FormManager } from './form-manager';
21
22
  import { PatientRegistration } from './patient-registration.component';
23
+ import { useInitialFormValuesLocal } from './patient-registration-hooks';
24
+ import { useMpiPatient } from './mpi/mpi-patient.resource';
22
25
 
23
26
  const mockSaveEncounter = jest.mocked(saveEncounter);
24
27
  const mockSavePatient = savePatient as jest.Mock;
25
28
  const mockShowSnackbar = jest.mocked(showSnackbar);
26
29
  const mockUseConfig = jest.mocked(useConfig<RegistrationConfig>);
27
30
  const mockUsePatient = jest.mocked(usePatient);
31
+ const mockUseParams = useParams as jest.Mock;
32
+ const mockUseInitialFormValues = jest.mocked(useInitialFormValuesLocal);
28
33
  const mockOpenmrsDatePicker = jest.mocked(OpenmrsDatePicker);
34
+ const mockUseMpiPatient = useMpiPatient as jest.Mock;
35
+
36
+ jest.mock('./mpi/mpi-patient.resource', () => ({
37
+ useMpiPatient: jest.fn(),
38
+ }));
29
39
 
30
40
  jest.mock('./field/field.resource', () => ({
31
41
  useConcept: jest.fn().mockImplementation((uuid: string) => {
@@ -86,7 +96,7 @@ jest.mock('./field/field.resource', () => ({
86
96
 
87
97
  jest.mock('react-router-dom', () => ({
88
98
  ...(jest.requireActual('react-router-dom') as any),
89
- useLocation: () => ({
99
+ useLocation: jest.fn().mockReturnValue({
90
100
  pathname: 'openmrs/spa/patient-registration',
91
101
  }),
92
102
  useHistory: () => [],
@@ -99,6 +109,14 @@ jest.mock('./patient-registration.resource', () => ({
99
109
  savePatient: jest.fn(),
100
110
  }));
101
111
 
112
+ jest.mock('./patient-registration-hooks', () => ({
113
+ ...jest.requireActual('./patient-registration-hooks'),
114
+ useInitialFormValuesLocal: jest.fn().mockReturnValue([{}, jest.fn()]),
115
+ useMpiInitialFormValues: jest.fn().mockReturnValue([{}, jest.fn()]),
116
+ useInitialAddressFieldValues: jest.fn().mockReturnValue([{}, jest.fn()]),
117
+ usePatientUuidMap: jest.fn().mockReturnValue([{}, jest.fn()]),
118
+ }));
119
+
102
120
  mockOpenmrsDatePicker.mockImplementation(({ id, labelText, value, onChange }) => {
103
121
  return (
104
122
  <>
@@ -126,10 +144,10 @@ const mockResourcesContextValue = {
126
144
  identifierTypes: [],
127
145
  };
128
146
 
129
- let mockOpenmrsConfig: RegistrationConfig = {
147
+ const mockOpenmrsConfig: RegistrationConfig = {
130
148
  sections: ['demographics', 'contact'],
131
149
  sectionDefinitions: [
132
- { id: 'demographics', name: 'Demographics', fields: ['name', 'gender', 'dob'] },
150
+ { id: 'demographics', name: 'Demographics', fields: ['name', 'gender', 'dob', 'id'] },
133
151
  { id: 'contact', name: 'Contact Info', fields: ['address'] },
134
152
  { id: 'relationships', name: 'Relationships', fields: ['relationship'] },
135
153
  ],
@@ -159,6 +177,13 @@ let mockOpenmrsConfig: RegistrationConfig = {
159
177
  value: 'male',
160
178
  label: 'Male',
161
179
  },
180
+ {
181
+ value: 'female',
182
+ label: 'Female',
183
+ },
184
+ ],
185
+ identifierMappings: [
186
+ { fhirIdentifierSystem: 'MPI OpenMRS ID', openmrsIdentifierTypeUuid: '8d793bee-c2cc-11de-8d13-0010c6dffd0f' },
162
187
  ],
163
188
  address: {
164
189
  useAddressHierarchy: {
@@ -174,17 +199,17 @@ let mockOpenmrsConfig: RegistrationConfig = {
174
199
  links: {
175
200
  submitButton: '#',
176
201
  },
177
- defaultPatientIdentifierTypes: [],
202
+ defaultPatientIdentifierTypes: ['8d793bee-c2cc-11de-8d13-0010c6dffd0f'],
178
203
  registrationObs: {
179
204
  encounterTypeUuid: null,
180
205
  encounterProviderRoleUuid: 'asdf',
181
206
  registrationFormUuid: null,
182
207
  },
208
+ freeTextFieldConceptUuid: '',
183
209
  };
184
-
185
210
  const path = `/patient/:patientUuid/edit`;
186
-
187
211
  const configWithObs = JSON.parse(JSON.stringify(mockOpenmrsConfig));
212
+
188
213
  configWithObs.fieldDefinitions = [
189
214
  {
190
215
  id: 'weight',
@@ -217,7 +242,6 @@ configWithObs.fieldDefinitions = [
217
242
  customConceptAnswers: [],
218
243
  },
219
244
  ];
220
-
221
245
  configWithObs.sectionDefinitions?.push({
222
246
  id: 'custom',
223
247
  name: 'Custom',
@@ -238,16 +262,14 @@ const fillRequiredFields = async () => {
238
262
  await user.type(familyNameInput, 'Gaihre');
239
263
  await user.clear(dateOfBirthInput);
240
264
  await user.type(dateOfBirthInput, '02/08/1993');
241
- user.click(genderInput);
265
+ await user.click(genderInput);
242
266
  };
243
267
 
244
- function Wrapper({ children }) {
245
- return (
246
- <ResourcesContext.Provider value={mockResourcesContextValue}>
247
- <Router>{children}</Router>
248
- </ResourcesContext.Provider>
249
- );
250
- }
268
+ const Wrapper = ({ children }) => (
269
+ <ResourcesContext.Provider value={mockResourcesContextValue}>
270
+ <Router>{children}</Router>
271
+ </ResourcesContext.Provider>
272
+ );
251
273
 
252
274
  describe('Registering a new patient', () => {
253
275
  beforeEach(() => {
@@ -256,17 +278,130 @@ describe('Registering a new patient', () => {
256
278
  ...mockOpenmrsConfig,
257
279
  });
258
280
  mockSavePatient.mockReturnValue({ data: { uuid: 'new-pt-uuid' }, ok: true });
281
+
282
+ mockUseMpiPatient.mockReturnValue({
283
+ isLoading: false,
284
+ patient: { data: null },
285
+ error: undefined,
286
+ });
259
287
  });
260
288
 
261
- it('renders without crashing', () => {
289
+ // TODO O3-3482: Fix this test case when OpenmrsDatePicker gets fixed on core
290
+ it.skip('saves the patient without extra info', async () => {
291
+ const user = userEvent.setup();
262
292
  render(<PatientRegistration isOffline={false} savePatientForm={jest.fn()} />, { wrapper: Wrapper });
293
+
294
+ await screen.findByRole('heading', { name: /create new patientnj/i });
295
+
296
+ await fillRequiredFields();
297
+ await user.click(await screen.findByText(/Register Patientsed/i));
298
+
299
+ expect(mockSavePatient).toHaveBeenCalledWith(
300
+ true,
301
+ {
302
+ addNameInLocalLanguage: false,
303
+ additionalFamilyName: '',
304
+ additionalGivenName: '',
305
+ additionalMiddleName: '',
306
+ address: { country: 'កម្ពុជា (Cambodia)' },
307
+ attributes: {},
308
+ birthdate: '1972-04-04',
309
+ birthdateEstimated: false,
310
+ deathCause: '',
311
+ nonCodedCauseOfDeath: '',
312
+ deathDate: undefined,
313
+ deathTime: undefined,
314
+ deathTimeFormat: 'AM',
315
+ familyName: 'Smith',
316
+ gender: 'male',
317
+ givenName: 'Eric',
318
+ identifiers: {
319
+ openMrsId: {
320
+ autoGeneration: false,
321
+ identifierName: 'OpenMRS ID',
322
+ identifierTypeUuid: '05a29f94-c0ed-11e2-94be-8c13b969e334',
323
+ identifierUuid: '1f0ad7a1-430f-4397-b571-59ea654a52db',
324
+ identifierValue: '100GEJ',
325
+ initialValue: '100GEJ',
326
+ preferred: true,
327
+ required: true,
328
+ selectedSource: null,
329
+ },
330
+ idCard: {
331
+ autoGeneration: false,
332
+ identifierName: 'ID Card',
333
+ identifierTypeUuid: 'b4143563-16cd-4439-b288-f83d61670fc8',
334
+ identifierUuid: '346d09b1-8509-43c6-9697-3b4d1ce06ad6',
335
+ identifierValue: '1234567890',
336
+ initialValue: '1234567890',
337
+ preferred: false,
338
+ required: false,
339
+ selectedSource: null,
340
+ },
341
+ },
342
+ isDead: false,
343
+ middleName: 'Johnson',
344
+ monthsEstimated: 0,
345
+ patientUuid: '8673ee4f-e2ab-4077-ba55-4980f408773ef',
346
+ relationships: [],
347
+ telephoneNumber: '',
348
+ unidentifiedPatient: undefined,
349
+ yearsEstimated: 0,
350
+ },
351
+ expect.anything(),
352
+ expect.anything(),
353
+ null,
354
+ undefined,
355
+ expect.anything(),
356
+ expect.anything(),
357
+ expect.anything(),
358
+ { patientSaved: false },
359
+ expect.anything(),
360
+ );
361
+
362
+ expect(mockSavePatient).toHaveBeenCalledWith(
363
+ expect.objectContaining({
364
+ identifiers: [], //TODO when the identifer story is finished: { identifier: '', identifierType: '05a29f94-c0ed-11e2-94be-8c13b969e334', location: '' },
365
+ person: {
366
+ addresses: expect.arrayContaining([expect.any(Object)]),
367
+ attributes: [],
368
+ birthdate: '1993-8-2',
369
+ birthdateEstimated: false,
370
+ gender: expect.stringMatching(/^M$/),
371
+ names: [{ givenName: 'Paul', middleName: '', familyName: 'Gaihre', preferred: true, uuid: undefined }],
372
+ dead: false,
373
+ uuid: expect.anything(),
374
+ },
375
+ uuid: expect.anything(),
376
+ }),
377
+ undefined,
378
+ );
263
379
  });
264
380
 
265
- it('has the expected sections', async () => {
381
+ it('should render all the required fields and sections', async () => {
266
382
  render(<PatientRegistration isOffline={false} savePatientForm={jest.fn()} />, { wrapper: Wrapper });
267
383
 
268
- expect(screen.getByRole('region', { name: /demographics section/i })).toBeInTheDocument();
269
- expect(screen.getByRole('region', { name: /contact info section/i })).toBeInTheDocument();
384
+ await screen.findByRole('heading', { name: /create new patient/i });
385
+
386
+ const demographicSection = screen.getByRole('region', { name: /demographics section/i });
387
+ const contactSection = screen.getByRole('region', { name: /contact info section/i });
388
+
389
+ expect(demographicSection).toBeInTheDocument();
390
+ expect(contactSection).toBeInTheDocument();
391
+ expect(screen.getByText(/jump to/i)).toBeInTheDocument();
392
+ expect(within(demographicSection).getByLabelText(/first name/i)).toBeInTheDocument();
393
+ expect(within(demographicSection).getByLabelText(/middle name \(optional\)/i)).toBeInTheDocument();
394
+ expect(within(demographicSection).getByLabelText(/family name/i)).toBeInTheDocument();
395
+ expect(within(demographicSection).getByLabelText(/date of birth/i)).toBeInTheDocument();
396
+ expect(within(demographicSection).getByRole('radio', { name: /^male$/i })).toBeInTheDocument();
397
+ expect(within(demographicSection).getByRole('radio', { name: /^female$/i })).toBeInTheDocument();
398
+ expect(within(demographicSection).getByText(/date of birth known\?/i)).toBeInTheDocument();
399
+ expect(within(demographicSection).getByLabelText(/date of birth/i)).toBeInTheDocument();
400
+
401
+ expect(within(contactSection).getByRole('heading', { name: /address/i })).toBeInTheDocument();
402
+
403
+ expect(screen.getByRole('button', { name: /register patient/i })).toBeInTheDocument();
404
+ expect(screen.getByRole('button', { name: /cancel/i })).toBeInTheDocument();
270
405
  });
271
406
 
272
407
  // TODO O3-3482: Fix this test case when OpenmrsDatePicker gets fixed on core
@@ -300,14 +435,12 @@ describe('Registering a new patient', () => {
300
435
 
301
436
  it('should not save the patient if validation fails', async () => {
302
437
  const user = userEvent.setup();
303
-
304
438
  const mockSavePatientForm = jest.fn();
439
+
305
440
  render(<PatientRegistration isOffline={false} savePatientForm={mockSavePatientForm} />, { wrapper: Wrapper });
306
441
 
307
- const givenNameInput = (await screen.findByLabelText('First Name')) as HTMLInputElement;
308
-
309
- await user.type(givenNameInput, '5');
310
- await user.click(screen.getByText(/Register Patient/i));
442
+ await screen.findByRole('heading', { name: /create new patient/i });
443
+ await user.click(screen.getByRole('button', { name: /register patient/i }));
311
444
 
312
445
  expect(mockSavePatientForm).not.toHaveBeenCalled();
313
446
  });
@@ -384,28 +517,97 @@ describe('Registering a new patient', () => {
384
517
  });
385
518
  });
386
519
 
387
- describe('Updating an existing patient record', () => {
520
+ describe('Import an MPI patient record', () => {
388
521
  beforeEach(() => {
389
- mockUseConfig.mockReturnValue(mockOpenmrsConfig);
522
+ mockUseConfig.mockReturnValue({
523
+ ...getDefaultsFromConfigSchema(esmPatientRegistrationSchema),
524
+ ...mockOpenmrsConfig,
525
+ });
526
+
527
+ mockUsePatient.mockImplementation(() => {
528
+ return {
529
+ error: null,
530
+ isLoading: false,
531
+ patient: null,
532
+ patientUuid: null,
533
+ };
534
+ });
390
535
  mockSavePatient.mockReturnValue({ data: { uuid: 'new-pt-uuid' }, ok: true });
536
+ mockUseParams.mockReturnValue({ patientUuid: mockPatient.id });
537
+ (useParams as jest.Mock).mockReturnValue({ patientUuid: undefined }),
538
+ (useLocation as jest.Mock).mockReturnValue({
539
+ pathname: 'openmrs/spa/patient-registration',
540
+ state: undefined,
541
+ key: '',
542
+ search: '?sourceRecord=55',
543
+ hash: '',
544
+ });
391
545
  });
392
546
 
393
- it('edits patient demographics', async () => {
547
+ it('fills patient demographics from MPI patient', async () => {
394
548
  const user = userEvent.setup();
395
- mockSavePatient.mockResolvedValue({} as FetchResponse);
396
-
397
- const mockUseParams = useParams as jest.Mock;
398
-
399
- mockUseParams.mockReturnValue({ patientUuid: mockPatient.id });
400
-
401
- mockUsePatient.mockReturnValue({
549
+ const mockSavePatientForm = jest.fn();
550
+ mockUseMpiPatient.mockReturnValue({
402
551
  isLoading: false,
403
- patient: mockPatient,
404
- patientUuid: mockPatient.id,
405
- error: null,
552
+ patient: { data: mockPatient },
553
+ error: undefined,
406
554
  });
407
555
 
408
- render(<PatientRegistration isOffline={false} savePatientForm={mockSavePatient} />, { wrapper: Wrapper });
556
+ mockUseInitialFormValues.mockReturnValue([
557
+ {
558
+ additionalFamilyName: '',
559
+ additionalGivenName: '',
560
+ additionalMiddleName: '',
561
+ addNameInLocalLanguage: false,
562
+ address: {},
563
+ birthdate: mockPatient.birthDate,
564
+ birthdateEstimated: false,
565
+ deathCause: '',
566
+ deathDate: undefined,
567
+ deathTime: undefined,
568
+ deathTimeFormat: 'AM',
569
+ familyName: mockPatient.name[0].family,
570
+ gender: mockPatient.gender,
571
+ givenName: mockPatient.name[0].given[0],
572
+ identifiers: {
573
+ openMrsId: {
574
+ autoGeneration: false,
575
+ identifierName: 'OpenMRS ID',
576
+ identifierTypeUuid: '05a29f94-c0ed-11e2-94be-8c13b969e334',
577
+ identifierUuid: '1f0ad7a1-430f-4397-b571-59ea654a52db',
578
+ identifierValue: '100GEJ',
579
+ initialValue: '100GEJ',
580
+ preferred: true,
581
+ required: true,
582
+ selectedSource: null,
583
+ },
584
+ idCard: {
585
+ autoGeneration: false,
586
+ identifierName: 'ID Card',
587
+ identifierTypeUuid: 'b4143563-16cd-4439-b288-f83d61670fc8',
588
+ identifierUuid: '346d09b1-8509-43c6-9697-3b4d1ce06ad6',
589
+ identifierValue: '1234567890',
590
+ initialValue: '1234567890',
591
+ preferred: false,
592
+ required: false,
593
+ selectedSource: null,
594
+ },
595
+ },
596
+ isDead: false,
597
+ middleName: '',
598
+ monthsEstimated: 0,
599
+ nonCodedCauseOfDeath: '',
600
+ patientUuid: mockPatient.id,
601
+ relationships: [],
602
+ telephoneNumber: '',
603
+ yearsEstimated: 0,
604
+ } as FormValues,
605
+ jest.fn(),
606
+ ]);
607
+ // eslint-disable-next-line testing-library/no-unnecessary-act
608
+ await act(async () => {
609
+ render(<PatientRegistration isOffline={false} savePatientForm={mockSavePatientForm} />, { wrapper: Wrapper });
610
+ });
409
611
 
410
612
  const givenNameInput: HTMLInputElement = screen.getByLabelText(/First Name/);
411
613
  const familyNameInput: HTMLInputElement = screen.getByLabelText(/Family Name/);
@@ -427,23 +629,19 @@ describe('Updating an existing patient record', () => {
427
629
  await user.type(givenNameInput, 'Eric');
428
630
  await user.type(middleNameInput, 'Johnson');
429
631
  await user.type(familyNameInput, 'Smith');
430
- await user.click(screen.getByText(/Update patient/i));
632
+ await user.click(screen.getByText(/Register patient/i));
431
633
 
432
- expect(mockSavePatient).toHaveBeenCalledWith(
433
- false,
634
+ expect(mockSavePatientForm).toHaveBeenCalledWith(
635
+ true,
434
636
  {
435
- '0': {
436
- oldIdentificationNumber: '100732HE',
437
- },
438
- '1': {
439
- openMrsId: '100GEJ',
440
- },
441
- addNameInLocalLanguage: undefined,
637
+ addNameInLocalLanguage: false,
442
638
  additionalFamilyName: '',
443
639
  additionalGivenName: '',
444
640
  additionalMiddleName: '',
445
- address: {},
446
- birthdate: new Date('1972-04-04T00:00:00.000Z'),
641
+ address: {
642
+ country: 'កម្ពុជា (Cambodia)',
643
+ },
644
+ birthdate: '1972-04-04',
447
645
  birthdateEstimated: false,
448
646
  deathCause: '',
449
647
  nonCodedCauseOfDeath: '',
@@ -451,9 +649,32 @@ describe('Updating an existing patient record', () => {
451
649
  deathTime: undefined,
452
650
  deathTimeFormat: 'AM',
453
651
  familyName: 'Smith',
454
- gender: expect.stringMatching(/male/i),
652
+ gender: 'male',
455
653
  givenName: 'Eric',
456
- identifiers: {},
654
+ identifiers: {
655
+ idCard: {
656
+ autoGeneration: false,
657
+ identifierName: 'ID Card',
658
+ identifierTypeUuid: 'b4143563-16cd-4439-b288-f83d61670fc8',
659
+ identifierUuid: '346d09b1-8509-43c6-9697-3b4d1ce06ad6',
660
+ identifierValue: '1234567890',
661
+ initialValue: '1234567890',
662
+ preferred: false,
663
+ required: false,
664
+ selectedSource: null,
665
+ },
666
+ openMrsId: {
667
+ autoGeneration: false,
668
+ identifierName: 'OpenMRS ID',
669
+ identifierTypeUuid: '05a29f94-c0ed-11e2-94be-8c13b969e334',
670
+ identifierUuid: '1f0ad7a1-430f-4397-b571-59ea654a52db',
671
+ identifierValue: '100GEJ',
672
+ initialValue: '100GEJ',
673
+ preferred: true,
674
+ required: true,
675
+ selectedSource: null,
676
+ },
677
+ },
457
678
  isDead: false,
458
679
  middleName: 'Johnson',
459
680
  monthsEstimated: 0,
@@ -475,3 +696,168 @@ describe('Updating an existing patient record', () => {
475
696
  );
476
697
  });
477
698
  });
699
+
700
+ describe('Updating an existing patient record', () => {
701
+ beforeEach(() => {
702
+ mockUseConfig.mockReturnValue({
703
+ ...getDefaultsFromConfigSchema(esmPatientRegistrationSchema),
704
+ ...mockOpenmrsConfig,
705
+ });
706
+ mockUsePatient.mockImplementation(() => {
707
+ return {
708
+ error: null,
709
+ isLoading: false,
710
+ patient: mockPatient,
711
+ patientUuid: mockPatient.id,
712
+ };
713
+ });
714
+ mockSavePatient.mockReturnValue({ data: { uuid: 'new-pt-uuid' }, ok: true });
715
+ mockUseParams.mockReturnValue({ patientUuid: mockPatient.id });
716
+ });
717
+
718
+ it('edits patient demographics', async () => {
719
+ const user = userEvent.setup();
720
+ const mockSavePatientForm = jest.fn();
721
+
722
+ mockUseInitialFormValues.mockReturnValue([
723
+ {
724
+ additionalFamilyName: '',
725
+ additionalGivenName: '',
726
+ additionalMiddleName: '',
727
+ addNameInLocalLanguage: false,
728
+ address: {},
729
+ birthdate: mockPatient.birthDate,
730
+ birthdateEstimated: false,
731
+ deathCause: '',
732
+ deathDate: undefined,
733
+ deathTime: undefined,
734
+ deathTimeFormat: 'AM',
735
+ familyName: mockPatient.name[0].family,
736
+ gender: mockPatient.gender,
737
+ givenName: mockPatient.name[0].given[0],
738
+ identifiers: {
739
+ openMrsId: {
740
+ autoGeneration: false,
741
+ identifierName: 'OpenMRS ID',
742
+ identifierTypeUuid: '05a29f94-c0ed-11e2-94be-8c13b969e334',
743
+ identifierUuid: '1f0ad7a1-430f-4397-b571-59ea654a52db',
744
+ identifierValue: '100GEJ',
745
+ initialValue: '100GEJ',
746
+ preferred: true,
747
+ required: true,
748
+ selectedSource: null,
749
+ },
750
+ idCard: {
751
+ autoGeneration: false,
752
+ identifierName: 'ID Card',
753
+ identifierTypeUuid: 'b4143563-16cd-4439-b288-f83d61670fc8',
754
+ identifierUuid: '346d09b1-8509-43c6-9697-3b4d1ce06ad6',
755
+ identifierValue: '1234567890',
756
+ initialValue: '1234567890',
757
+ preferred: false,
758
+ required: false,
759
+ selectedSource: null,
760
+ },
761
+ },
762
+ isDead: false,
763
+ middleName: '',
764
+ monthsEstimated: 0,
765
+ nonCodedCauseOfDeath: '',
766
+ patientUuid: mockPatient.id,
767
+ relationships: [],
768
+ telephoneNumber: '',
769
+ yearsEstimated: 0,
770
+ } as FormValues,
771
+ jest.fn(),
772
+ ]);
773
+
774
+ render(<PatientRegistration isOffline={false} savePatientForm={mockSavePatientForm} />, { wrapper: Wrapper });
775
+
776
+ await screen.findByRole('heading', { name: /edit patient details/i });
777
+
778
+ expect(screen.queryByRole('button', { name: /register patient/i })).not.toBeInTheDocument();
779
+ expect(screen.getByRole('button', { name: /update patient/i })).toBeInTheDocument();
780
+ expect(screen.getByRole('button', { name: /cancel/i })).toBeInTheDocument();
781
+
782
+ expect(screen.getByLabelText(/first name/i)).toHaveValue(mockPatient.name[0].given[0]);
783
+ expect(screen.getByLabelText(/family name/i)).toHaveValue(mockPatient.name[0].family);
784
+ expect(screen.getByLabelText(/date of birth/i)).toHaveValue('04/04/1972');
785
+ expect(
786
+ screen.getByRole('radio', {
787
+ name: /^male$/i,
788
+ }),
789
+ ).toBeChecked();
790
+ expect(
791
+ screen.getByRole('radio', {
792
+ name: /^female$/i,
793
+ }),
794
+ ).not.toBeChecked();
795
+ expect(screen.getAllByRole('tab', { name: /yes/i })).toHaveLength(2);
796
+
797
+ await user.click(screen.getByRole('button', { name: /update patient/i }));
798
+
799
+ expect(mockSavePatientForm).toHaveBeenCalledWith(
800
+ false,
801
+ {
802
+ addNameInLocalLanguage: false,
803
+ additionalFamilyName: '',
804
+ additionalGivenName: '',
805
+ additionalMiddleName: '',
806
+ address: {
807
+ country: 'កម្ពុជា (Cambodia)',
808
+ },
809
+ birthdate: '1972-04-04',
810
+ birthdateEstimated: false,
811
+ deathCause: '',
812
+ nonCodedCauseOfDeath: '',
813
+ deathDate: undefined,
814
+ deathTime: undefined,
815
+ deathTimeFormat: 'AM',
816
+ familyName: 'Wilson',
817
+ gender: 'male',
818
+ givenName: 'John',
819
+ identifiers: {
820
+ idCard: {
821
+ autoGeneration: false,
822
+ identifierName: 'ID Card',
823
+ identifierTypeUuid: 'b4143563-16cd-4439-b288-f83d61670fc8',
824
+ identifierUuid: '346d09b1-8509-43c6-9697-3b4d1ce06ad6',
825
+ identifierValue: '1234567890',
826
+ initialValue: '1234567890',
827
+ preferred: false,
828
+ required: false,
829
+ selectedSource: null,
830
+ },
831
+ openMrsId: {
832
+ autoGeneration: false,
833
+ identifierName: 'OpenMRS ID',
834
+ identifierTypeUuid: '05a29f94-c0ed-11e2-94be-8c13b969e334',
835
+ identifierUuid: '1f0ad7a1-430f-4397-b571-59ea654a52db',
836
+ identifierValue: '100GEJ',
837
+ initialValue: '100GEJ',
838
+ preferred: true,
839
+ required: true,
840
+ selectedSource: null,
841
+ },
842
+ },
843
+ isDead: false,
844
+ middleName: '',
845
+ monthsEstimated: 0,
846
+ patientUuid: '8673ee4f-e2ab-4077-ba55-4980f408773e',
847
+ relationships: [],
848
+ telephoneNumber: '',
849
+ unidentifiedPatient: undefined,
850
+ yearsEstimated: 0,
851
+ },
852
+ expect.anything(),
853
+ expect.anything(),
854
+ null,
855
+ undefined,
856
+ expect.anything(),
857
+ expect.anything(),
858
+ expect.anything(),
859
+ { patientSaved: false },
860
+ expect.anything(),
861
+ );
862
+ });
863
+ });
@@ -1,8 +1,8 @@
1
1
  import React, { useContext, useEffect } from 'react';
2
- import styles from './../section.scss';
3
2
  import { useField } from 'formik';
4
- import { PatientRegistrationContext } from '../../patient-registration-context';
5
3
  import { Field } from '../../field/field.component';
4
+ import { PatientRegistrationContext } from '../../patient-registration-context';
5
+ import styles from './../section.scss';
6
6
 
7
7
  export interface DemographicsSectionProps {
8
8
  fields: Array<string>;
@@ -18,7 +18,7 @@ export const DemographicsSection: React.FC<DemographicsSectionProps> = ({ fields
18
18
  setFieldValue('additionalMiddleName', '');
19
19
  setFieldValue('additionalFamilyName', '');
20
20
  }
21
- }, [field.value, meta.touched]);
21
+ }, [field.value, meta.touched, setFieldValue]);
22
22
 
23
23
  return (
24
24
  <section className={styles.formSection} aria-label="Demographics Section">
@@ -40,7 +40,7 @@ const RelationshipView: React.FC<RelationshipViewProps> = ({
40
40
  }) => {
41
41
  const { t } = useTranslation();
42
42
  const { setFieldValue } = React.useContext(PatientRegistrationContext);
43
- const [isInvalid, setIsInvalid] = useState<boolean>(false);
43
+ const [isInvalid, setIsInvalid] = useState(false);
44
44
  const newRelationship = !relationship.uuid;
45
45
 
46
46
  const handleRelationshipTypeChange = useCallback(