@ampath/esm-patient-registration-app 6.0.1-pre.6

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 (176) hide show
  1. package/.turbo/turbo-build.log +41 -0
  2. package/README.md +7 -0
  3. package/dist/130.js +2 -0
  4. package/dist/130.js.LICENSE.txt +3 -0
  5. package/dist/130.js.map +1 -0
  6. package/dist/152.js +1 -0
  7. package/dist/152.js.map +1 -0
  8. package/dist/249.js +2 -0
  9. package/dist/249.js.LICENSE.txt +46 -0
  10. package/dist/249.js.map +1 -0
  11. package/dist/255.js +2 -0
  12. package/dist/255.js.LICENSE.txt +9 -0
  13. package/dist/255.js.map +1 -0
  14. package/dist/271.js +1 -0
  15. package/dist/303.js +1 -0
  16. package/dist/303.js.map +1 -0
  17. package/dist/319.js +1 -0
  18. package/dist/365.js +1 -0
  19. package/dist/365.js.map +1 -0
  20. package/dist/460.js +1 -0
  21. package/dist/525.js +1 -0
  22. package/dist/525.js.map +1 -0
  23. package/dist/537.js +1 -0
  24. package/dist/537.js.map +1 -0
  25. package/dist/574.js +1 -0
  26. package/dist/591.js +2 -0
  27. package/dist/591.js.LICENSE.txt +32 -0
  28. package/dist/591.js.map +1 -0
  29. package/dist/621.js +1 -0
  30. package/dist/621.js.map +1 -0
  31. package/dist/644.js +1 -0
  32. package/dist/729.js +1 -0
  33. package/dist/729.js.map +1 -0
  34. package/dist/735.js +1 -0
  35. package/dist/735.js.map +1 -0
  36. package/dist/757.js +1 -0
  37. package/dist/784.js +2 -0
  38. package/dist/784.js.LICENSE.txt +9 -0
  39. package/dist/784.js.map +1 -0
  40. package/dist/788.js +1 -0
  41. package/dist/807.js +1 -0
  42. package/dist/833.js +1 -0
  43. package/dist/879.js +1 -0
  44. package/dist/879.js.map +1 -0
  45. package/dist/ampath-esm-patient-registration-app.js +1 -0
  46. package/dist/ampath-esm-patient-registration-app.js.buildmanifest.json +649 -0
  47. package/dist/ampath-esm-patient-registration-app.js.map +1 -0
  48. package/dist/main.js +2 -0
  49. package/dist/main.js.LICENSE.txt +56 -0
  50. package/dist/main.js.map +1 -0
  51. package/dist/routes.json +1 -0
  52. package/docs/images/patient-registration-hierarchy.png +0 -0
  53. package/jest.config.js +3 -0
  54. package/package.json +61 -0
  55. package/src/add-patient-link.scss +3 -0
  56. package/src/add-patient-link.test.tsx +20 -0
  57. package/src/add-patient-link.tsx +21 -0
  58. package/src/config-schema.ts +410 -0
  59. package/src/constants.ts +14 -0
  60. package/src/declarations.d.ts +6 -0
  61. package/src/index.ts +71 -0
  62. package/src/nav-link.test.tsx +13 -0
  63. package/src/nav-link.tsx +10 -0
  64. package/src/offline.resources.ts +155 -0
  65. package/src/offline.ts +91 -0
  66. package/src/patient-registration/before-save-prompt.tsx +73 -0
  67. package/src/patient-registration/date-util.ts +52 -0
  68. package/src/patient-registration/field/__mocks__/field.resource.ts +60 -0
  69. package/src/patient-registration/field/address/address-field.component.tsx +153 -0
  70. package/src/patient-registration/field/address/address-hierarchy-levels.component.tsx +73 -0
  71. package/src/patient-registration/field/address/address-hierarchy.resource.tsx +157 -0
  72. package/src/patient-registration/field/address/address-search.component.tsx +85 -0
  73. package/src/patient-registration/field/address/address-search.scss +53 -0
  74. package/src/patient-registration/field/address/custom-address-field.component.tsx +31 -0
  75. package/src/patient-registration/field/address/tests/address-hierarchy.test.tsx +214 -0
  76. package/src/patient-registration/field/address/tests/address-search-component.test.tsx +135 -0
  77. package/src/patient-registration/field/custom-field.component.tsx +25 -0
  78. package/src/patient-registration/field/dob/dob.component.tsx +159 -0
  79. package/src/patient-registration/field/dob/dob.test.tsx +75 -0
  80. package/src/patient-registration/field/field.component.tsx +47 -0
  81. package/src/patient-registration/field/field.resource.ts +35 -0
  82. package/src/patient-registration/field/field.scss +127 -0
  83. package/src/patient-registration/field/field.test.tsx +294 -0
  84. package/src/patient-registration/field/gender/gender-field.component.tsx +49 -0
  85. package/src/patient-registration/field/gender/gender-field.test.tsx +59 -0
  86. package/src/patient-registration/field/id/id-field.component.tsx +144 -0
  87. package/src/patient-registration/field/id/id-field.test.tsx +107 -0
  88. package/src/patient-registration/field/id/identifier-selection-overlay.component.tsx +198 -0
  89. package/src/patient-registration/field/id/identifier-selection.scss +37 -0
  90. package/src/patient-registration/field/name/name-field.component.tsx +142 -0
  91. package/src/patient-registration/field/obs/obs-field.component.tsx +204 -0
  92. package/src/patient-registration/field/obs/obs-field.test.tsx +205 -0
  93. package/src/patient-registration/field/person-attributes/coded-attributes.component.tsx +60 -0
  94. package/src/patient-registration/field/person-attributes/coded-person-attribute-field.component.tsx +116 -0
  95. package/src/patient-registration/field/person-attributes/coded-person-attribute-field.test.tsx +127 -0
  96. package/src/patient-registration/field/person-attributes/person-attribute-field.component.tsx +88 -0
  97. package/src/patient-registration/field/person-attributes/person-attribute-field.test.tsx +187 -0
  98. package/src/patient-registration/field/person-attributes/person-attributes.resource.ts +20 -0
  99. package/src/patient-registration/field/person-attributes/text-person-attribute-field.component.tsx +58 -0
  100. package/src/patient-registration/field/person-attributes/text-person-attribute-field.test.tsx +88 -0
  101. package/src/patient-registration/field/phone/phone-field.component.tsx +16 -0
  102. package/src/patient-registration/form-manager.test.ts +67 -0
  103. package/src/patient-registration/form-manager.ts +414 -0
  104. package/src/patient-registration/input/basic-input/input/input.component.tsx +179 -0
  105. package/src/patient-registration/input/basic-input/input/input.test.tsx +72 -0
  106. package/src/patient-registration/input/basic-input/select/select-input.component.tsx +32 -0
  107. package/src/patient-registration/input/basic-input/select/select-input.test.tsx +49 -0
  108. package/src/patient-registration/input/combo-input/combo-input.component.tsx +128 -0
  109. package/src/patient-registration/input/combo-input/selection-tick.component.tsx +20 -0
  110. package/src/patient-registration/input/custom-input/autosuggest/autosuggest.component.tsx +187 -0
  111. package/src/patient-registration/input/custom-input/autosuggest/autosuggest.scss +62 -0
  112. package/src/patient-registration/input/custom-input/autosuggest/autosuggest.test.tsx +132 -0
  113. package/src/patient-registration/input/custom-input/identifier/identifier-input.component.tsx +156 -0
  114. package/src/patient-registration/input/custom-input/identifier/identifier-input.test.tsx +107 -0
  115. package/src/patient-registration/input/custom-input/identifier/utils.test.ts +81 -0
  116. package/src/patient-registration/input/custom-input/identifier/utils.ts +19 -0
  117. package/src/patient-registration/input/dummy-data/dummy-data-input.component.tsx +53 -0
  118. package/src/patient-registration/input/dummy-data/dummy-data-input.test.tsx +43 -0
  119. package/src/patient-registration/input/input.scss +118 -0
  120. package/src/patient-registration/patient-registration-context.ts +24 -0
  121. package/src/patient-registration/patient-registration-hooks.ts +287 -0
  122. package/src/patient-registration/patient-registration-utils.ts +216 -0
  123. package/src/patient-registration/patient-registration.component.tsx +240 -0
  124. package/src/patient-registration/patient-registration.resource.test.tsx +26 -0
  125. package/src/patient-registration/patient-registration.resource.ts +250 -0
  126. package/src/patient-registration/patient-registration.scss +122 -0
  127. package/src/patient-registration/patient-registration.test.tsx +471 -0
  128. package/src/patient-registration/patient-registration.types.ts +318 -0
  129. package/src/patient-registration/section/death-info/death-info-section.component.tsx +31 -0
  130. package/src/patient-registration/section/death-info/death-info-section.test.tsx +64 -0
  131. package/src/patient-registration/section/demographics/demographics-section.component.tsx +30 -0
  132. package/src/patient-registration/section/demographics/demographics-section.test.tsx +83 -0
  133. package/src/patient-registration/section/generic-section.component.tsx +17 -0
  134. package/src/patient-registration/section/patient-relationships/relationships-section.component.tsx +235 -0
  135. package/src/patient-registration/section/patient-relationships/relationships-section.test.tsx +100 -0
  136. package/src/patient-registration/section/patient-relationships/relationships.resource.tsx +78 -0
  137. package/src/patient-registration/section/patient-relationships/relationships.scss +35 -0
  138. package/src/patient-registration/section/section-wrapper.component.tsx +40 -0
  139. package/src/patient-registration/section/section.component.tsx +23 -0
  140. package/src/patient-registration/section/section.scss +1 -0
  141. package/src/patient-registration/ui-components/overlay/overlay.component.tsx +51 -0
  142. package/src/patient-registration/ui-components/overlay/overlay.scss +63 -0
  143. package/src/patient-registration/validation/patient-registration-validation.test.tsx +157 -0
  144. package/src/patient-registration/validation/patient-registration-validation.tsx +60 -0
  145. package/src/patient-verification/client-registry-constants.ts +13 -0
  146. package/src/patient-verification/client-registry.component.tsx +66 -0
  147. package/src/patient-verification/client-registry.scss +1 -0
  148. package/src/patient-verification/utils.tsx +56 -0
  149. package/src/patient-verification/verification-modal.scss +20 -0
  150. package/src/patient-verification/verification.component.tsx +48 -0
  151. package/src/resource.ts +12 -0
  152. package/src/root.component.tsx +63 -0
  153. package/src/root.scss +7 -0
  154. package/src/root.test.tsx +32 -0
  155. package/src/routes.json +66 -0
  156. package/src/widgets/cancel-patient-edit.component.tsx +37 -0
  157. package/src/widgets/cancel-patient-edit.test.tsx +27 -0
  158. package/src/widgets/delete-identifier-confirmation-modal.test.tsx +34 -0
  159. package/src/widgets/delete-identifier-confirmation-modal.tsx +41 -0
  160. package/src/widgets/delete-identifier-modal.scss +34 -0
  161. package/src/widgets/display-photo.component.tsx +30 -0
  162. package/src/widgets/display-photo.test.tsx +37 -0
  163. package/src/widgets/edit-patient-details-button.component.tsx +34 -0
  164. package/src/widgets/edit-patient-details-button.scss +3 -0
  165. package/src/widgets/edit-patient-details-button.test.tsx +41 -0
  166. package/translations/am.json +97 -0
  167. package/translations/ar.json +97 -0
  168. package/translations/en.json +103 -0
  169. package/translations/es.json +97 -0
  170. package/translations/fr.json +97 -0
  171. package/translations/he.json +97 -0
  172. package/translations/km.json +97 -0
  173. package/translations/zh.json +89 -0
  174. package/translations/zh_CN.json +89 -0
  175. package/tsconfig.json +5 -0
  176. package/webpack.config.js +1 -0
@@ -0,0 +1,471 @@
1
+ import React from 'react';
2
+ import { BrowserRouter as Router, useParams } from 'react-router-dom';
3
+ import { render, screen, within } from '@testing-library/react';
4
+ import userEvent from '@testing-library/user-event';
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';
8
+ import { FormManager } from './form-manager';
9
+ import { ResourcesContext } from '../offline.resources';
10
+ import { PatientRegistration } from './patient-registration.component';
11
+ import { saveEncounter, savePatient } from './patient-registration.resource';
12
+ import { mockedAddressTemplate } from '__mocks__';
13
+ import { mockPatient } from 'tools';
14
+
15
+ const mockedUseConfig = useConfig as jest.Mock;
16
+ const mockedUsePatient = usePatient as jest.Mock;
17
+ const mockedSaveEncounter = saveEncounter as jest.Mock;
18
+ const mockedSavePatient = savePatient as jest.Mock;
19
+ const mockedShowSnackbar = showSnackbar as jest.Mock;
20
+
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
+ }));
77
+
78
+ jest.mock('@openmrs/esm-framework', () => {
79
+ const originalModule = jest.requireActual('@openmrs/esm-framework');
80
+
81
+ return {
82
+ ...originalModule,
83
+ validator: jest.fn(),
84
+ };
85
+ });
86
+
87
+ jest.mock('react-router-dom', () => ({
88
+ ...(jest.requireActual('react-router-dom') as any),
89
+ useLocation: () => ({
90
+ pathname: 'openmrs/spa/patient-registration',
91
+ }),
92
+ useHistory: () => [],
93
+ useParams: jest.fn().mockReturnValue({ patientUuid: undefined }),
94
+ }));
95
+
96
+ jest.mock('./patient-registration.resource', () => {
97
+ const originalModule = jest.requireActual('./patient-registration.resource');
98
+
99
+ return {
100
+ ...originalModule,
101
+ saveEncounter: jest.fn(),
102
+ savePatient: jest.fn(),
103
+ };
104
+ });
105
+
106
+ jest.mock('@openmrs/esm-framework', () => {
107
+ const originalModule = jest.requireActual('@openmrs/esm-framework');
108
+
109
+ return {
110
+ ...originalModule,
111
+ validator: jest.fn(),
112
+ getLocale: jest.fn().mockReturnValue('en'),
113
+ };
114
+ });
115
+
116
+ const mockResourcesContextValue = {
117
+ addressTemplate: mockedAddressTemplate as AddressTemplate,
118
+ currentSession: {
119
+ authenticated: true,
120
+ sessionId: 'JSESSION',
121
+ currentProvider: { uuid: 'provider-uuid', identifier: 'PRO-123' },
122
+ },
123
+ relationshipTypes: [],
124
+ identifierTypes: [],
125
+ };
126
+
127
+ let mockOpenmrsConfig: RegistrationConfig = {
128
+ sections: ['demographics', 'contact'],
129
+ sectionDefinitions: [
130
+ { id: 'demographics', name: 'Demographics', fields: ['name', 'gender', 'dob'] },
131
+ { id: 'contact', name: 'Contact Info', fields: ['address'] },
132
+ { id: 'relationships', name: 'Relationships', fields: ['relationship'] },
133
+ ],
134
+ fieldDefinitions: [],
135
+ fieldConfigurations: {
136
+ dateOfBirth: {
137
+ allowEstimatedDateOfBirth: true,
138
+ useEstimatedDateOfBirth: {
139
+ enabled: true,
140
+ dayOfMonth: new Date().getDay(),
141
+ month: new Date().getMonth(),
142
+ },
143
+ },
144
+ name: {
145
+ displayMiddleName: true,
146
+ allowUnidentifiedPatients: true,
147
+ defaultUnknownGivenName: 'UNKNOWN',
148
+ defaultUnknownFamilyName: 'UNKNOWN',
149
+ displayReverseFieldOrder: false,
150
+ displayCapturePhoto: true,
151
+ },
152
+ gender: [
153
+ {
154
+ value: 'male',
155
+ label: 'Male',
156
+ },
157
+ ],
158
+ address: {
159
+ useAddressHierarchy: {
160
+ enabled: true,
161
+ useQuickSearch: true,
162
+ searchAddressByLevel: true,
163
+ },
164
+ },
165
+ },
166
+ concepts: {
167
+ patientPhotoUuid: '736e8771-e501-4615-bfa7-570c03f4bef5',
168
+ },
169
+ links: {
170
+ submitButton: '#',
171
+ },
172
+ defaultPatientIdentifierTypes: [],
173
+ registrationObs: {
174
+ encounterTypeUuid: null,
175
+ encounterProviderRoleUuid: 'asdf',
176
+ registrationFormUuid: null,
177
+ },
178
+ };
179
+
180
+ const path = `/patient/:patientUuid/edit`;
181
+
182
+ const configWithObs = JSON.parse(JSON.stringify(mockOpenmrsConfig));
183
+ configWithObs.fieldDefinitions = [
184
+ {
185
+ id: 'weight',
186
+ type: 'obs',
187
+ label: null,
188
+ uuid: 'weight-uuid',
189
+ placeholder: '',
190
+ validation: { required: false, matches: null },
191
+ answerConceptSetUuid: null,
192
+ customConceptAnswers: [],
193
+ },
194
+ {
195
+ id: 'chief complaint',
196
+ type: 'obs',
197
+ label: null,
198
+ uuid: 'chief-complaint-uuid',
199
+ placeholder: '',
200
+ validation: { required: false, matches: null },
201
+ answerConceptSetUuid: null,
202
+ customConceptAnswers: [],
203
+ },
204
+ {
205
+ id: 'nationality',
206
+ type: 'obs',
207
+ label: null,
208
+ uuid: 'nationality-uuid',
209
+ placeholder: '',
210
+ validation: { required: false, matches: null },
211
+ answerConceptSetUuid: null,
212
+ customConceptAnswers: [],
213
+ },
214
+ ];
215
+
216
+ configWithObs.sectionDefinitions?.push({
217
+ id: 'custom',
218
+ name: 'Custom',
219
+ fields: ['weight', 'chief complaint', 'nationality'],
220
+ });
221
+ configWithObs.sections.push('custom');
222
+ configWithObs.registrationObs.encounterTypeUuid = 'reg-enc-uuid';
223
+
224
+ const fillRequiredFields = async () => {
225
+ const user = userEvent.setup();
226
+
227
+ const demographicsSection = await screen.findByLabelText('Demographics Section');
228
+ const givenNameInput = within(demographicsSection).getByLabelText(/first/i) as HTMLInputElement;
229
+ const familyNameInput = within(demographicsSection).getByLabelText(/family/i) as HTMLInputElement;
230
+ const dateOfBirthInput = within(demographicsSection).getByLabelText(/date of birth/i) as HTMLInputElement;
231
+ const genderInput = within(demographicsSection).getByLabelText(/Male/) as HTMLSelectElement;
232
+
233
+ await user.type(givenNameInput, 'Paul');
234
+ await user.type(familyNameInput, 'Gaihre');
235
+ await user.clear(dateOfBirthInput);
236
+ await user.type(dateOfBirthInput, '02/08/1993');
237
+ user.click(genderInput);
238
+ };
239
+
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
+ });
256
+
257
+ it('renders without crashing', () => {
258
+ render(<PatientRegistration isOffline={false} savePatientForm={jest.fn()} />, { wrapper: Wrapper });
259
+ });
260
+
261
+ it('has the expected sections', async () => {
262
+ render(<PatientRegistration isOffline={false} savePatientForm={jest.fn()} />, { wrapper: Wrapper });
263
+
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,
273
+ });
274
+
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
+ });
295
+
296
+ it('should not save the patient if validation fails', async () => {
297
+ const user = userEvent.setup();
298
+
299
+ const mockedSavePatientForm = jest.fn();
300
+ render(<PatientRegistration isOffline={false} savePatientForm={mockedSavePatientForm} />, { wrapper: Wrapper });
301
+
302
+ const givenNameInput = (await screen.findByLabelText('First Name')) as HTMLInputElement;
303
+
304
+ await user.type(givenNameInput, '5');
305
+ await user.click(screen.getByText('Register Patient'));
306
+
307
+ expect(mockedSavePatientForm).not.toHaveBeenCalled();
308
+ });
309
+
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,
318
+ });
319
+
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();
348
+
349
+ mockedUseConfig.mockReturnValue(configWithObs);
350
+
351
+ render(<PatientRegistration isOffline={false} savePatientForm={FormManager.savePatientFormOnline} />, {
352
+ wrapper: Wrapper,
353
+ });
354
+
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');
359
+
360
+ mockedSaveEncounter.mockRejectedValue({ status: 400, responseBody: { error: { message: 'an error message' } } });
361
+
362
+ const registerPatientButton = screen.getByText('Register Patient');
363
+
364
+ await user.click(registerPatientButton);
365
+
366
+ expect(mockedSavePatient).toHaveBeenCalledTimes(1);
367
+ expect(mockedSaveEncounter).toHaveBeenCalledTimes(1);
368
+
369
+ expect(mockedShowSnackbar).toHaveBeenCalledWith(expect.objectContaining({ subtitle: 'an error message' })),
370
+ mockedSaveEncounter.mockResolvedValue({});
371
+
372
+ await user.click(registerPatientButton);
373
+ expect(mockedSavePatient).toHaveBeenCalledTimes(2);
374
+ expect(mockedSaveEncounter).toHaveBeenCalledTimes(2);
375
+
376
+ expect(mockedShowSnackbar).toHaveBeenCalledWith(expect.objectContaining({ kind: 'success' }));
377
+ });
378
+ });
379
+
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
+ });
388
+
389
+ it('edits patient demographics', async () => {
390
+ const user = userEvent.setup();
391
+
392
+ mockedSavePatient.mockResolvedValue({});
393
+
394
+ const mockedUseParams = useParams as jest.Mock;
395
+
396
+ mockedUseParams.mockReturnValue({ patientUuid: mockPatient.id });
397
+
398
+ mockedUsePatient.mockReturnValue({
399
+ isLoading: false,
400
+ patient: mockPatient,
401
+ patientUuid: mockPatient.id,
402
+ error: null,
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
+ );
470
+ });
471
+ });