@ampath/esm-patient-registration-app 6.0.1-pre.96 → 9.2.0-next.12

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 (288) hide show
  1. package/dist/1119.js +1 -0
  2. package/dist/1197.js +1 -0
  3. package/dist/21.js +1 -0
  4. package/dist/21.js.map +1 -0
  5. package/dist/2146.js +1 -0
  6. package/dist/2372.js +1 -0
  7. package/dist/2372.js.map +1 -0
  8. package/dist/2470.js +1 -0
  9. package/dist/2470.js.map +1 -0
  10. package/dist/2690.js +1 -0
  11. package/dist/2913.js +2 -0
  12. package/dist/{913.js.LICENSE.txt → 2913.js.LICENSE.txt} +3 -23
  13. package/dist/2913.js.map +1 -0
  14. package/dist/3093.js +1 -0
  15. package/dist/3093.js.map +1 -0
  16. package/dist/3099.js +1 -0
  17. package/dist/3144.js +2 -0
  18. package/dist/3144.js.LICENSE.txt +19 -0
  19. package/dist/3144.js.map +1 -0
  20. package/dist/320.js +2 -0
  21. package/dist/{876.js.LICENSE.txt → 320.js.LICENSE.txt} +2 -3
  22. package/dist/320.js.map +1 -0
  23. package/dist/3464.js +1 -0
  24. package/dist/3464.js.map +1 -0
  25. package/dist/3474.js +2 -0
  26. package/dist/3474.js.LICENSE.txt +8 -0
  27. package/dist/3474.js.map +1 -0
  28. package/dist/3584.js +1 -0
  29. package/dist/4041.js +2 -0
  30. package/dist/4041.js.map +1 -0
  31. package/dist/4055.js +1 -0
  32. package/dist/4132.js +1 -0
  33. package/dist/4300.js +1 -0
  34. package/dist/4335.js +1 -0
  35. package/dist/4463.js +1 -0
  36. package/dist/4463.js.map +1 -0
  37. package/dist/4618.js +1 -0
  38. package/dist/4652.js +1 -0
  39. package/dist/4944.js +1 -0
  40. package/dist/5173.js +1 -0
  41. package/dist/5220.js +2 -0
  42. package/dist/5220.js.LICENSE.txt +29 -0
  43. package/dist/5220.js.map +1 -0
  44. package/dist/5241.js +1 -0
  45. package/dist/5442.js +1 -0
  46. package/dist/5661.js +1 -0
  47. package/dist/6022.js +1 -0
  48. package/dist/6078.js +2 -0
  49. package/dist/6078.js.LICENSE.txt +9 -0
  50. package/dist/6078.js.map +1 -0
  51. package/dist/627.js +1 -0
  52. package/dist/627.js.map +1 -0
  53. package/dist/6276.js +1 -0
  54. package/dist/6276.js.map +1 -0
  55. package/dist/6468.js +1 -0
  56. package/dist/6679.js +1 -0
  57. package/dist/6737.js +2 -0
  58. package/dist/6737.js.LICENSE.txt +9 -0
  59. package/dist/6737.js.map +1 -0
  60. package/dist/6840.js +1 -0
  61. package/dist/6859.js +1 -0
  62. package/dist/7092.js +1 -0
  63. package/dist/7092.js.map +1 -0
  64. package/dist/7097.js +1 -0
  65. package/dist/7159.js +1 -0
  66. package/dist/723.js +1 -0
  67. package/dist/7495.js +2 -0
  68. package/dist/7495.js.LICENSE.txt +9 -0
  69. package/dist/7495.js.map +1 -0
  70. package/dist/7617.js +1 -0
  71. package/dist/795.js +1 -0
  72. package/dist/8163.js +1 -0
  73. package/dist/8349.js +1 -0
  74. package/dist/8404.js +2 -0
  75. package/dist/{629.js.LICENSE.txt → 8404.js.LICENSE.txt} +9 -3
  76. package/dist/8404.js.map +1 -0
  77. package/dist/8434.js +1 -0
  78. package/dist/8434.js.map +1 -0
  79. package/dist/8618.js +1 -0
  80. package/dist/89.js +2 -0
  81. package/dist/89.js.LICENSE.txt +9 -0
  82. package/dist/89.js.map +1 -0
  83. package/dist/890.js +1 -0
  84. package/dist/9214.js +1 -0
  85. package/dist/9538.js +1 -0
  86. package/dist/9569.js +1 -0
  87. package/dist/986.js +1 -0
  88. package/dist/9876.js +1 -0
  89. package/dist/9876.js.map +1 -0
  90. package/dist/9879.js +1 -0
  91. package/dist/9895.js +1 -0
  92. package/dist/9900.js +1 -0
  93. package/dist/9913.js +1 -0
  94. package/dist/main.js +1 -1
  95. package/dist/main.js.LICENSE.txt +36 -1
  96. package/dist/main.js.map +1 -1
  97. package/dist/openmrs-esm-patient-registration-app.js +1 -0
  98. package/dist/openmrs-esm-patient-registration-app.js.buildmanifest.json +1576 -0
  99. package/dist/openmrs-esm-patient-registration-app.js.map +1 -0
  100. package/dist/routes.json +1 -1
  101. package/package.json +16 -15
  102. package/src/{add-patient-link.tsx → add-patient-link.extension.tsx} +4 -2
  103. package/src/add-patient-link.test.tsx +6 -10
  104. package/src/config-schema.ts +109 -55
  105. package/src/constants.ts +1 -1
  106. package/src/declarations.d.ts +5 -4
  107. package/src/index.ts +10 -29
  108. package/src/nav-link.test.tsx +3 -3
  109. package/src/offline.resources.ts +26 -18
  110. package/src/patient-photo.extension.tsx +3 -1
  111. package/src/patient-registration/field/address/address-field.component.tsx +58 -37
  112. package/src/patient-registration/field/address/address-hierarchy-levels.component.tsx +16 -18
  113. package/src/patient-registration/field/address/address-hierarchy.resource.tsx +3 -3
  114. package/src/patient-registration/field/address/address-hierarchy.test.tsx +290 -0
  115. package/src/patient-registration/field/address/address-search.component.tsx +7 -5
  116. package/src/patient-registration/field/address/address-search.scss +5 -5
  117. package/src/patient-registration/field/address/address-search.test.tsx +140 -0
  118. package/src/patient-registration/field/cause-of-death/cause-of-death.component.tsx +98 -0
  119. package/src/patient-registration/field/custom-field.component.tsx +3 -9
  120. package/src/patient-registration/field/date-and-time-of-death/date-and-time-of-death.component.tsx +84 -0
  121. package/src/patient-registration/field/dob/dob.component.tsx +55 -50
  122. package/src/patient-registration/field/dob/dob.test.tsx +90 -0
  123. package/src/patient-registration/field/field.component.tsx +12 -6
  124. package/src/patient-registration/field/field.resource.ts +11 -4
  125. package/src/patient-registration/field/field.scss +69 -25
  126. package/src/patient-registration/field/field.test.tsx +329 -0
  127. package/src/patient-registration/field/gender/gender-field.component.tsx +14 -9
  128. package/src/patient-registration/field/gender/gender-field.test.tsx +73 -33
  129. package/src/patient-registration/field/id/id-field.component.tsx +24 -23
  130. package/src/patient-registration/field/id/id-field.test.tsx +147 -0
  131. package/src/patient-registration/field/id/identifier-selection-overlay.component.tsx +12 -10
  132. package/src/patient-registration/field/id/identifier-selection.scss +12 -8
  133. package/src/patient-registration/field/name/name-field.component.tsx +10 -5
  134. package/src/patient-registration/field/obs/obs-field.component.tsx +59 -2
  135. package/src/patient-registration/field/obs/obs-field.test.tsx +133 -39
  136. package/src/patient-registration/field/person-attributes/coded-person-attribute-field.component.tsx +3 -3
  137. package/src/patient-registration/field/person-attributes/coded-person-attribute-field.test.tsx +141 -0
  138. package/src/patient-registration/field/person-attributes/location-person-attribute-field.component.tsx +105 -0
  139. package/src/patient-registration/field/person-attributes/location-person-attribute-field.resource.tsx +48 -0
  140. package/src/patient-registration/field/person-attributes/person-attribute-field.component.tsx +19 -22
  141. package/src/patient-registration/field/person-attributes/person-attribute-field.test.tsx +193 -0
  142. package/src/patient-registration/field/person-attributes/text-person-attribute-field.test.tsx +90 -0
  143. package/src/patient-registration/form-manager.test.ts +91 -0
  144. package/src/patient-registration/form-manager.ts +49 -23
  145. package/src/patient-registration/input/basic-input/input/input.component.tsx +6 -2
  146. package/src/patient-registration/input/basic-input/select/select-input.test.tsx +49 -0
  147. package/src/patient-registration/input/custom-input/autosuggest/autosuggest.scss +5 -5
  148. package/src/patient-registration/input/custom-input/autosuggest/autosuggest.test.tsx +164 -0
  149. package/src/patient-registration/input/custom-input/identifier/identifier-input.component.tsx +73 -36
  150. package/src/patient-registration/input/custom-input/identifier/identifier-input.test.tsx +335 -0
  151. package/src/patient-registration/input/dummy-data/dummy-data-input.component.tsx +3 -0
  152. package/src/patient-registration/input/dummy-data/dummy-data-input.test.tsx +2 -11
  153. package/src/patient-registration/input/input.scss +17 -13
  154. package/src/patient-registration/patient-registration-context.ts +22 -11
  155. package/src/patient-registration/patient-registration-hooks.ts +158 -193
  156. package/src/patient-registration/patient-registration-utils.test.ts +33 -0
  157. package/src/patient-registration/patient-registration-utils.ts +11 -13
  158. package/src/patient-registration/patient-registration.component.tsx +87 -103
  159. package/src/patient-registration/{patient-registration.resource.testt.tsx → patient-registration.resource.test.tsx} +0 -4
  160. package/src/patient-registration/patient-registration.resource.ts +27 -3
  161. package/src/patient-registration/patient-registration.scss +27 -38
  162. package/src/patient-registration/patient-registration.test.tsx +579 -0
  163. package/src/patient-registration/patient-registration.types.ts +23 -25
  164. package/src/patient-registration/section/death-info/death-info-section.component.tsx +22 -17
  165. package/src/patient-registration/section/death-info/death-info-section.test.tsx +47 -0
  166. package/src/patient-registration/section/demographics/demographics-section.component.tsx +5 -5
  167. package/src/patient-registration/section/demographics/demographics-section.test.tsx +98 -0
  168. package/src/patient-registration/section/patient-relationships/relationships-section.component.tsx +8 -7
  169. package/src/patient-registration/section/patient-relationships/relationships-section.test.tsx +113 -0
  170. package/src/patient-registration/section/patient-relationships/relationships.resource.tsx +28 -28
  171. package/src/patient-registration/section/patient-relationships/relationships.scss +4 -4
  172. package/src/patient-registration/section/section-wrapper.component.tsx +1 -1
  173. package/src/patient-registration/section/section.component.tsx +1 -1
  174. package/src/patient-registration/section/section.scss +21 -1
  175. package/src/patient-registration/ui-components/overlay/overlay.scss +8 -8
  176. package/src/patient-registration/validation/{patient-registration-validation.test.tsx → patient-registration-validation.test.ts} +71 -23
  177. package/src/patient-registration/validation/patient-registration-validation.ts +123 -0
  178. package/src/resources-context.ts +14 -0
  179. package/src/root.component.tsx +3 -3
  180. package/src/routes.json +10 -24
  181. package/src/widgets/cancel-patient-edit.modal.tsx +33 -0
  182. package/src/widgets/cancel-patient-edit.test.tsx +22 -0
  183. package/src/widgets/delete-identifier-confirmation.modal.tsx +48 -0
  184. package/src/widgets/{delete-identifier-confirmation-modal.testt.tsx → delete-identifier-confirmation.test.tsx} +5 -7
  185. package/src/widgets/edit-patient-details-button.component.tsx +0 -1
  186. package/src/widgets/edit-patient-details-button.test.tsx +35 -0
  187. package/translations/am.json +43 -35
  188. package/translations/ar.json +41 -33
  189. package/translations/ar_SY.json +119 -0
  190. package/translations/bn.json +119 -0
  191. package/translations/de.json +119 -0
  192. package/translations/en.json +44 -42
  193. package/translations/en_US.json +119 -0
  194. package/translations/es.json +69 -57
  195. package/translations/es_MX.json +119 -0
  196. package/translations/fr.json +74 -58
  197. package/translations/he.json +44 -40
  198. package/translations/hi.json +119 -0
  199. package/translations/hi_IN.json +119 -0
  200. package/translations/id.json +119 -0
  201. package/translations/it.json +119 -0
  202. package/translations/ka.json +119 -0
  203. package/translations/km.json +44 -40
  204. package/translations/ku.json +119 -0
  205. package/translations/ky.json +119 -0
  206. package/translations/lg.json +119 -0
  207. package/translations/ne.json +119 -0
  208. package/translations/pl.json +119 -0
  209. package/translations/pt.json +119 -0
  210. package/translations/pt_BR.json +119 -0
  211. package/translations/qu.json +119 -0
  212. package/translations/ro_RO.json +119 -0
  213. package/translations/ru_RU.json +119 -0
  214. package/translations/si.json +119 -0
  215. package/translations/sw.json +119 -0
  216. package/translations/sw_KE.json +119 -0
  217. package/translations/tr.json +119 -0
  218. package/translations/tr_TR.json +119 -0
  219. package/translations/uk.json +119 -0
  220. package/translations/uz.json +119 -0
  221. package/translations/uz@Latn.json +119 -0
  222. package/translations/uz_UZ.json +119 -0
  223. package/translations/vi.json +119 -0
  224. package/translations/zh.json +45 -23
  225. package/translations/zh_CN.json +39 -17
  226. package/.turbo/turbo-build.log +0 -40
  227. package/dist/132.js +0 -1
  228. package/dist/197.js +0 -1
  229. package/dist/236.js +0 -1
  230. package/dist/236.js.map +0 -1
  231. package/dist/300.js +0 -1
  232. package/dist/335.js +0 -1
  233. package/dist/372.js +0 -1
  234. package/dist/372.js.map +0 -1
  235. package/dist/41.js +0 -2
  236. package/dist/41.js.map +0 -1
  237. package/dist/449.js +0 -1
  238. package/dist/449.js.map +0 -1
  239. package/dist/464.js +0 -1
  240. package/dist/464.js.map +0 -1
  241. package/dist/495.js +0 -1
  242. package/dist/495.js.map +0 -1
  243. package/dist/55.js +0 -1
  244. package/dist/56.js +0 -1
  245. package/dist/56.js.map +0 -1
  246. package/dist/621.js +0 -1
  247. package/dist/621.js.map +0 -1
  248. package/dist/629.js +0 -2
  249. package/dist/629.js.map +0 -1
  250. package/dist/652.js +0 -1
  251. package/dist/661.js +0 -1
  252. package/dist/757.js +0 -1
  253. package/dist/757.js.map +0 -1
  254. package/dist/828.js +0 -1
  255. package/dist/828.js.map +0 -1
  256. package/dist/830.js +0 -1
  257. package/dist/830.js.map +0 -1
  258. package/dist/831.js +0 -2
  259. package/dist/831.js.LICENSE.txt +0 -3
  260. package/dist/831.js.map +0 -1
  261. package/dist/876.js +0 -2
  262. package/dist/876.js.map +0 -1
  263. package/dist/879.js +0 -1
  264. package/dist/913.js +0 -2
  265. package/dist/913.js.map +0 -1
  266. package/dist/927.js +0 -1
  267. package/dist/927.js.map +0 -1
  268. package/dist/99.js +0 -1
  269. package/dist/ampath-esm-patient-registration-app.js +0 -1
  270. package/dist/ampath-esm-patient-registration-app.js.buildmanifest.json +0 -694
  271. package/dist/ampath-esm-patient-registration-app.js.map +0 -1
  272. package/src/patient-registration/date-util.ts +0 -52
  273. package/src/patient-registration/field/person-attributes/custom-person-attribute-field.component.tsx +0 -56
  274. package/src/patient-registration/validation/patient-registration-validation.tsx +0 -60
  275. package/src/patient-verification/assets/counties.json +0 -236
  276. package/src/patient-verification/assets/verification-assets.ts +0 -11
  277. package/src/patient-verification/patient-verification-hook.tsx +0 -176
  278. package/src/patient-verification/patient-verification-utils.ts +0 -179
  279. package/src/patient-verification/patient-verification.component.tsx +0 -124
  280. package/src/patient-verification/patient-verification.scss +0 -25
  281. package/src/patient-verification/verification-modal/confirm-prompt.component.tsx +0 -72
  282. package/src/patient-verification/verification-modal/empty-prompt.component.tsx +0 -35
  283. package/src/patient-verification/verification-types.ts +0 -50
  284. package/src/widgets/cancel-patient-edit.component.tsx +0 -37
  285. package/src/widgets/delete-identifier-confirmation-modal.tsx +0 -41
  286. package/src/widgets/delete-identifier-modal.scss +0 -34
  287. /package/dist/{41.js.LICENSE.txt → 4041.js.LICENSE.txt} +0 -0
  288. /package/src/patient-registration/input/custom-input/identifier/{utils.testt.ts → utils.test.ts} +0 -0
@@ -0,0 +1,579 @@
1
+ import React from 'react';
2
+ import userEvent from '@testing-library/user-event';
3
+ import { useParams } from 'react-router-dom';
4
+ import { screen, within } from '@testing-library/react';
5
+ import {
6
+ type FetchResponse,
7
+ getDefaultsFromConfigSchema,
8
+ showSnackbar,
9
+ useConfig,
10
+ usePatient,
11
+ } from '@openmrs/esm-framework';
12
+ import type { AddressTemplate, Encounter, FormValues } from './patient-registration.types';
13
+ import { mockedAddressTemplate } from '__mocks__';
14
+ import { mockPatient, renderWithContext } from 'tools';
15
+ import { saveEncounter, savePatient } from './patient-registration.resource';
16
+ import { esmPatientRegistrationSchema, type RegistrationConfig } from '../config-schema';
17
+ import { FormManager } from './form-manager';
18
+ import { PatientRegistration } from './patient-registration.component';
19
+ import { useInitialFormValues } from './patient-registration-hooks';
20
+ import { ResourcesContextProvider } from '../resources-context';
21
+
22
+ const mockSaveEncounter = jest.mocked(saveEncounter);
23
+ const mockSavePatient = savePatient as jest.Mock;
24
+ const mockShowSnackbar = jest.mocked(showSnackbar);
25
+ const mockUseConfig = jest.mocked(useConfig<RegistrationConfig>);
26
+ const mockUsePatient = jest.mocked(usePatient);
27
+ const mockUseParams = useParams as jest.Mock;
28
+ const mockUseInitialFormValues = jest.mocked(useInitialFormValues);
29
+
30
+ jest.mock('./field/field.resource', () => ({
31
+ useConcept: jest.fn().mockImplementation((uuid: string) => {
32
+ let data;
33
+ if (uuid == 'weight-uuid') {
34
+ data = {
35
+ uuid: 'weight-uuid',
36
+ display: 'Weight (kg)',
37
+ datatype: { display: 'Numeric', uuid: 'num' },
38
+ answers: [],
39
+ setMembers: [],
40
+ };
41
+ } else if (uuid == 'chief-complaint-uuid') {
42
+ data = {
43
+ uuid: 'chief-complaint-uuid',
44
+ display: 'Chief Complaint',
45
+ datatype: { display: 'Text', uuid: 'txt' },
46
+ answers: [],
47
+ setMembers: [],
48
+ };
49
+ } else if (uuid == 'nationality-uuid') {
50
+ data = {
51
+ uuid: 'nationality-uuid',
52
+ display: 'Nationality',
53
+ datatype: { display: 'Coded', uuid: 'cdd' },
54
+ answers: [
55
+ { display: 'USA', uuid: 'usa' },
56
+ { display: 'Mexico', uuid: 'mex' },
57
+ ],
58
+ setMembers: [],
59
+ };
60
+ }
61
+ return {
62
+ data: data ?? null,
63
+ isLoading: !data,
64
+ };
65
+ }),
66
+ useConceptAnswers: jest.fn().mockImplementation((uuid: string) => {
67
+ if (uuid == 'nationality-uuid') {
68
+ return {
69
+ data: [
70
+ { display: 'USA', uuid: 'usa' },
71
+ { display: 'Mexico', uuid: 'mex' },
72
+ ],
73
+ isLoading: false,
74
+ };
75
+ } else if (uuid == 'other-countries-uuid') {
76
+ return {
77
+ data: [
78
+ { display: 'Kenya', uuid: 'ke' },
79
+ { display: 'Uganda', uuid: 'ug' },
80
+ ],
81
+ isLoading: false,
82
+ };
83
+ }
84
+ }),
85
+ }));
86
+
87
+ jest.mock('react-router-dom', () => ({
88
+ ...jest.requireActual('react-router-dom'),
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
+ ...jest.requireActual('./patient-registration.resource'),
98
+ saveEncounter: jest.fn(),
99
+ savePatient: jest.fn(),
100
+ }));
101
+
102
+ jest.mock('./patient-registration-hooks', () => ({
103
+ ...jest.requireActual('./patient-registration-hooks'),
104
+ useInitialFormValues: jest.fn().mockReturnValue([{}, jest.fn()]),
105
+ useInitialAddressFieldValues: jest.fn().mockReturnValue([{}, jest.fn()]),
106
+ usePatientUuidMap: jest.fn().mockReturnValue([{}, jest.fn()]),
107
+ }));
108
+
109
+ const mockResourcesContextValue = {
110
+ addressTemplate: mockedAddressTemplate as AddressTemplate,
111
+ currentSession: {
112
+ authenticated: true,
113
+ sessionId: 'JSESSION',
114
+ currentProvider: { uuid: 'provider-uuid', identifier: 'PRO-123' },
115
+ },
116
+ relationshipTypes: [],
117
+ identifierTypes: [],
118
+ };
119
+
120
+ const mockOpenmrsConfig: RegistrationConfig = {
121
+ sections: ['demographics', 'contact'],
122
+ sectionDefinitions: [
123
+ { id: 'demographics', name: 'Demographics', fields: ['name', 'gender', 'dob'] },
124
+ { id: 'contact', name: 'Contact Info', fields: ['address'] },
125
+ { id: 'relationships', name: 'Relationships', fields: ['relationship'] },
126
+ ],
127
+ fieldDefinitions: [],
128
+ fieldConfigurations: {
129
+ phone: {
130
+ personAttributeUuid: '14d4f066-15f5-102d-96e4-000c29c2a5d7',
131
+ },
132
+ dateOfBirth: {
133
+ allowEstimatedDateOfBirth: true,
134
+ useEstimatedDateOfBirth: {
135
+ enabled: true,
136
+ dayOfMonth: new Date().getDate(),
137
+ month: new Date().getMonth(),
138
+ },
139
+ },
140
+ name: {
141
+ displayMiddleName: true,
142
+ allowUnidentifiedPatients: true,
143
+ defaultUnknownGivenName: 'UNKNOWN',
144
+ defaultUnknownFamilyName: 'UNKNOWN',
145
+ displayReverseFieldOrder: false,
146
+ displayCapturePhoto: true,
147
+ },
148
+ gender: [
149
+ {
150
+ value: 'male',
151
+ label: 'Male',
152
+ },
153
+ {
154
+ value: 'female',
155
+ label: 'Female',
156
+ },
157
+ ],
158
+ address: {
159
+ useAddressHierarchy: {
160
+ enabled: true,
161
+ useQuickSearch: true,
162
+ searchAddressByLevel: true,
163
+ },
164
+ },
165
+ causeOfDeath: {
166
+ conceptUuid: 'cause-of-death-concept-uuid',
167
+ },
168
+ },
169
+ links: {
170
+ submitButton: '#',
171
+ },
172
+ defaultPatientIdentifierTypes: [],
173
+ registrationObs: {
174
+ encounterTypeUuid: null,
175
+ encounterProviderRoleUuid: 'asdf',
176
+ registrationFormUuid: null,
177
+ },
178
+ freeTextFieldConceptUuid: '',
179
+ };
180
+ const configWithObs = JSON.parse(JSON.stringify(mockOpenmrsConfig));
181
+
182
+ configWithObs.fieldDefinitions = [
183
+ {
184
+ id: 'weight',
185
+ type: 'obs',
186
+ label: null,
187
+ uuid: 'weight-uuid',
188
+ placeholder: '',
189
+ validation: { required: false, matches: null },
190
+ answerConceptSetUuid: null,
191
+ customConceptAnswers: [],
192
+ },
193
+ {
194
+ id: 'chief complaint',
195
+ type: 'obs',
196
+ label: null,
197
+ uuid: 'chief-complaint-uuid',
198
+ placeholder: '',
199
+ validation: { required: false, matches: null },
200
+ answerConceptSetUuid: null,
201
+ customConceptAnswers: [],
202
+ },
203
+ {
204
+ id: 'nationality',
205
+ type: 'obs',
206
+ label: null,
207
+ uuid: 'nationality-uuid',
208
+ placeholder: '',
209
+ validation: { required: false, matches: null },
210
+ answerConceptSetUuid: null,
211
+ customConceptAnswers: [],
212
+ },
213
+ ];
214
+ configWithObs.sectionDefinitions?.push({
215
+ id: 'custom',
216
+ name: 'Custom',
217
+ fields: ['weight', 'chief complaint', 'nationality'],
218
+ });
219
+ configWithObs.sections.push('custom');
220
+ configWithObs.registrationObs.encounterTypeUuid = 'reg-enc-uuid';
221
+
222
+ const fillRequiredFields = async () => {
223
+ const user = userEvent.setup();
224
+
225
+ const demographicsSection = await screen.findByLabelText('Demographics Section');
226
+ const givenNameInput = within(demographicsSection).getByLabelText(/first/i) as HTMLInputElement;
227
+ const familyNameInput = within(demographicsSection).getByLabelText(/family/i) as HTMLInputElement;
228
+ const dateInput = within(demographicsSection).getByRole('spinbutton', {
229
+ name: /day, date of birth/i,
230
+ }) as HTMLInputElement;
231
+ const monthInput = within(demographicsSection).getByRole('spinbutton', {
232
+ name: /month, date of birth/i,
233
+ }) as HTMLInputElement;
234
+ const yearInput = within(demographicsSection).getByRole('spinbutton', {
235
+ name: /year, date of birth/i,
236
+ }) as HTMLInputElement;
237
+ const genderInput = within(demographicsSection).getByLabelText(/Male/) as HTMLSelectElement;
238
+ await user.type(givenNameInput, 'Paul');
239
+ await user.type(familyNameInput, 'Gaihre');
240
+ await user.clear(dateInput);
241
+ await user.type(dateInput, '02');
242
+ await user.clear(monthInput);
243
+ await user.type(monthInput, '08');
244
+ await user.clear(yearInput);
245
+ await user.type(yearInput, '1993');
246
+ await user.click(genderInput);
247
+ };
248
+
249
+ describe('Registering a new patient', () => {
250
+ beforeEach(() => {
251
+ mockUseConfig.mockReturnValue({
252
+ ...getDefaultsFromConfigSchema(esmPatientRegistrationSchema),
253
+ ...mockOpenmrsConfig,
254
+ });
255
+ mockSavePatient.mockReturnValue({ data: { uuid: 'new-pt-uuid' }, ok: true });
256
+ });
257
+
258
+ it('should render all the required fields and sections', async () => {
259
+ renderWithContext(
260
+ <PatientRegistration isOffline={false} savePatientForm={jest.fn()} />,
261
+ ResourcesContextProvider,
262
+ mockResourcesContextValue,
263
+ );
264
+
265
+ await screen.findByRole('heading', { name: /create new patient/i });
266
+
267
+ const demographicSection = screen.getByRole('region', { name: /demographics section/i });
268
+ const contactSection = screen.getByRole('region', { name: /contact info section/i });
269
+
270
+ expect(demographicSection).toBeInTheDocument();
271
+ expect(contactSection).toBeInTheDocument();
272
+ expect(screen.getByText(/jump to/i)).toBeInTheDocument();
273
+ expect(within(demographicSection).getByLabelText(/first name/i)).toBeInTheDocument();
274
+ expect(within(demographicSection).getByLabelText(/middle name \(optional\)/i)).toBeInTheDocument();
275
+ expect(within(demographicSection).getByLabelText(/family name/i)).toBeInTheDocument();
276
+ const dateOfBirthInput = within(demographicSection).getByLabelText(/date of birth/i);
277
+ expect(dateOfBirthInput).toBeInTheDocument();
278
+ expect(within(demographicSection).getByRole('radio', { name: /^male$/i })).toBeInTheDocument();
279
+ expect(within(demographicSection).getByRole('radio', { name: /^female$/i })).toBeInTheDocument();
280
+ expect(within(demographicSection).getByText(/date of birth known\?/i)).toBeInTheDocument();
281
+
282
+ expect(within(contactSection).getByRole('heading', { name: /address/i })).toBeInTheDocument();
283
+
284
+ expect(screen.getByRole('button', { name: /register patient/i })).toBeInTheDocument();
285
+ expect(screen.getByRole('button', { name: /cancel/i })).toBeInTheDocument();
286
+ });
287
+
288
+ // FIXME the register patient button is missing
289
+ it.skip('saves the patient without extra info', async () => {
290
+ const user = userEvent.setup();
291
+
292
+ renderWithContext(
293
+ <PatientRegistration isOffline={false} savePatientForm={FormManager.savePatientFormOnline} />,
294
+ ResourcesContextProvider,
295
+ mockResourcesContextValue,
296
+ );
297
+
298
+ await fillRequiredFields();
299
+ await user.click(await screen.findByText(/Register Patient/i));
300
+ expect(mockSavePatient).toHaveBeenCalledWith(
301
+ expect.objectContaining({
302
+ identifiers: [], //TODO when the identifer story is finished: { identifier: '', identifierType: '05a29f94-c0ed-11e2-94be-8c13b969e334', location: '' },
303
+ person: {
304
+ addresses: expect.arrayContaining([expect.any(Object)]),
305
+ attributes: [],
306
+ birthdate: '1993-8-2',
307
+ birthdateEstimated: false,
308
+ gender: expect.stringMatching(/^M$/),
309
+ names: [{ givenName: 'Paul', middleName: '', familyName: 'Gaihre', preferred: true, uuid: undefined }],
310
+ dead: false,
311
+ uuid: expect.anything(),
312
+ },
313
+ uuid: expect.anything(),
314
+ }),
315
+ undefined,
316
+ );
317
+ });
318
+
319
+ it('should not save the patient if validation fails', async () => {
320
+ const user = userEvent.setup();
321
+ const mockSavePatientForm = jest.fn();
322
+
323
+ renderWithContext(
324
+ <PatientRegistration isOffline={false} savePatientForm={mockSavePatientForm} />,
325
+ ResourcesContextProvider,
326
+ mockResourcesContextValue,
327
+ );
328
+
329
+ await screen.findByRole('heading', { name: /create new patient/i });
330
+ await user.click(screen.getByRole('button', { name: /register patient/i }));
331
+
332
+ expect(mockSavePatientForm).not.toHaveBeenCalled();
333
+ });
334
+
335
+ // FIXME: the register patient button is missing
336
+ it.skip('renders and saves registration obs', async () => {
337
+ const user = userEvent.setup();
338
+
339
+ mockSaveEncounter.mockResolvedValue({} as unknown as FetchResponse);
340
+ mockUseConfig.mockReturnValue(configWithObs);
341
+
342
+ renderWithContext(
343
+ <PatientRegistration isOffline={false} savePatientForm={FormManager.savePatientFormOnline} />,
344
+ ResourcesContextProvider,
345
+ mockResourcesContextValue,
346
+ );
347
+
348
+ await fillRequiredFields();
349
+ const customSection = screen.getByLabelText('Custom Section');
350
+ const weight = within(customSection).getByLabelText('Weight (kg) (optional)');
351
+ await user.type(weight, '50');
352
+ const complaint = within(customSection).getByLabelText('Chief Complaint (optional)');
353
+ await user.type(complaint, 'sad');
354
+ const nationality = within(customSection).getByLabelText('Nationality');
355
+ await user.selectOptions(nationality, 'USA');
356
+
357
+ await user.click(screen.getByText(/Register Patient/i));
358
+
359
+ expect(mockSavePatient).toHaveBeenCalled();
360
+
361
+ expect(mockSaveEncounter).toHaveBeenCalledWith(
362
+ expect.objectContaining<Partial<Encounter>>({
363
+ encounterType: 'reg-enc-uuid',
364
+ patient: 'new-pt-uuid',
365
+ obs: [
366
+ { concept: 'weight-uuid', value: 50 },
367
+ { concept: 'chief-complaint-uuid', value: 'sad' },
368
+ { concept: 'nationality-uuid', value: 'usa' },
369
+ ],
370
+ }),
371
+ );
372
+ });
373
+
374
+ // FIXME register patient button is missing
375
+ it.skip('retries saving registration obs after a failed attempt', async () => {
376
+ const user = userEvent.setup();
377
+
378
+ mockUseConfig.mockReturnValue(configWithObs);
379
+
380
+ renderWithContext(
381
+ <PatientRegistration isOffline={false} savePatientForm={FormManager.savePatientFormOnline} />,
382
+ ResourcesContextProvider,
383
+ mockResourcesContextValue,
384
+ );
385
+
386
+ await fillRequiredFields();
387
+ const customSection = screen.getByLabelText('Custom Section');
388
+ const weight = within(customSection).getByLabelText('Weight (kg) (optional)');
389
+ await user.type(weight, '-999');
390
+
391
+ mockSaveEncounter.mockRejectedValue({ status: 400, responseBody: { error: { message: 'an error message' } } });
392
+
393
+ const registerPatientButton = screen.getByText(/Register Patient/i);
394
+
395
+ await user.click(registerPatientButton);
396
+
397
+ expect(mockSavePatient).toHaveBeenCalledTimes(1);
398
+ expect(mockSaveEncounter).toHaveBeenCalledTimes(1);
399
+
400
+ (expect(mockShowSnackbar).toHaveBeenCalledWith(expect.objectContaining({ subtitle: 'an error message' })),
401
+ mockSaveEncounter.mockResolvedValue({} as FetchResponse));
402
+
403
+ await user.click(registerPatientButton);
404
+ expect(mockSavePatient).toHaveBeenCalledTimes(2);
405
+ expect(mockSaveEncounter).toHaveBeenCalledTimes(2);
406
+
407
+ expect(mockShowSnackbar).toHaveBeenCalledWith(expect.objectContaining({ kind: 'success' }));
408
+ });
409
+ });
410
+
411
+ describe('Updating an existing patient record', () => {
412
+ beforeEach(() => {
413
+ mockUseConfig.mockReturnValue({
414
+ ...getDefaultsFromConfigSchema(esmPatientRegistrationSchema),
415
+ ...mockOpenmrsConfig,
416
+ });
417
+ mockUsePatient.mockImplementation(() => {
418
+ return {
419
+ error: null,
420
+ isLoading: false,
421
+ patient: mockPatient,
422
+ patientUuid: mockPatient.id,
423
+ };
424
+ });
425
+ mockSavePatient.mockReturnValue({ data: { uuid: 'new-pt-uuid' }, ok: true });
426
+ mockUseParams.mockReturnValue({ patientUuid: mockPatient.id });
427
+ });
428
+
429
+ it('edits patient demographics', async () => {
430
+ const user = userEvent.setup();
431
+ const mockSavePatientForm = jest.fn();
432
+
433
+ mockUseInitialFormValues.mockReturnValue([
434
+ {
435
+ additionalFamilyName: '',
436
+ additionalGivenName: '',
437
+ additionalMiddleName: '',
438
+ addNameInLocalLanguage: false,
439
+ address: {},
440
+ birthdate: mockPatient.birthDate,
441
+ birthdateEstimated: false,
442
+ deathCause: '',
443
+ deathDate: undefined,
444
+ deathTime: undefined,
445
+ deathTimeFormat: 'AM',
446
+ familyName: mockPatient.name[0].family,
447
+ gender: mockPatient.gender,
448
+ givenName: mockPatient.name[0].given[0],
449
+ identifiers: {
450
+ openMrsId: {
451
+ autoGeneration: false,
452
+ identifierName: 'OpenMRS ID',
453
+ identifierTypeUuid: '05a29f94-c0ed-11e2-94be-8c13b969e334',
454
+ identifierUuid: '1f0ad7a1-430f-4397-b571-59ea654a52db',
455
+ identifierValue: '100GEJ',
456
+ initialValue: '100GEJ',
457
+ preferred: true,
458
+ required: true,
459
+ selectedSource: null,
460
+ },
461
+ idCard: {
462
+ autoGeneration: false,
463
+ identifierName: 'ID Card',
464
+ identifierTypeUuid: 'b4143563-16cd-4439-b288-f83d61670fc8',
465
+ identifierUuid: '346d09b1-8509-43c6-9697-3b4d1ce06ad6',
466
+ identifierValue: '1234567890',
467
+ initialValue: '1234567890',
468
+ preferred: false,
469
+ required: false,
470
+ selectedSource: null,
471
+ },
472
+ },
473
+ isDead: false,
474
+ middleName: '',
475
+ monthsEstimated: 0,
476
+ nonCodedCauseOfDeath: '',
477
+ patientUuid: mockPatient.id,
478
+ relationships: [],
479
+ telephoneNumber: '',
480
+ yearsEstimated: 0,
481
+ } as FormValues,
482
+ jest.fn(),
483
+ ]);
484
+
485
+ renderWithContext(
486
+ <PatientRegistration isOffline={false} savePatientForm={mockSavePatientForm} />,
487
+ ResourcesContextProvider,
488
+ mockResourcesContextValue,
489
+ );
490
+
491
+ await screen.findByRole('heading', { name: /edit patient details/i });
492
+
493
+ expect(screen.queryByRole('button', { name: /register patient/i })).not.toBeInTheDocument();
494
+ expect(screen.getByRole('button', { name: /update patient/i })).toBeInTheDocument();
495
+ expect(screen.getByRole('button', { name: /cancel/i })).toBeInTheDocument();
496
+
497
+ expect(screen.getByLabelText(/first name/i)).toHaveValue(mockPatient.name[0].given[0]);
498
+ expect(screen.getByLabelText(/family name/i)).toHaveValue(mockPatient.name[0].family);
499
+ // FIXME: Fix the mock so that this value is visible
500
+ // expect(screen.getByLabelText(/date of birth/i)).toHaveValue(mockPatient.birthDate);
501
+ expect(
502
+ screen.getByRole('radio', {
503
+ name: /^male$/i,
504
+ }),
505
+ ).toBeChecked();
506
+ expect(
507
+ screen.getByRole('radio', {
508
+ name: /^female$/i,
509
+ }),
510
+ ).not.toBeChecked();
511
+ expect(screen.getAllByRole('tab', { name: /yes/i })).toHaveLength(2);
512
+
513
+ await user.click(screen.getByRole('button', { name: /update patient/i }));
514
+
515
+ expect(mockSavePatientForm).toHaveBeenCalledWith(
516
+ false,
517
+ {
518
+ addNameInLocalLanguage: false,
519
+ additionalFamilyName: '',
520
+ additionalGivenName: '',
521
+ additionalMiddleName: '',
522
+ address: {
523
+ country: 'កម្ពុជា (Cambodia)',
524
+ },
525
+ birthdate: '1972-04-04',
526
+ birthdateEstimated: false,
527
+ deathCause: '',
528
+ nonCodedCauseOfDeath: '',
529
+ deathDate: undefined,
530
+ deathTime: undefined,
531
+ deathTimeFormat: 'AM',
532
+ familyName: 'Wilson',
533
+ gender: 'male',
534
+ givenName: 'John',
535
+ identifiers: {
536
+ idCard: {
537
+ autoGeneration: false,
538
+ identifierName: 'ID Card',
539
+ identifierTypeUuid: 'b4143563-16cd-4439-b288-f83d61670fc8',
540
+ identifierUuid: '346d09b1-8509-43c6-9697-3b4d1ce06ad6',
541
+ identifierValue: '1234567890',
542
+ initialValue: '1234567890',
543
+ preferred: false,
544
+ required: false,
545
+ selectedSource: null,
546
+ },
547
+ openMrsId: {
548
+ autoGeneration: false,
549
+ identifierName: 'OpenMRS ID',
550
+ identifierTypeUuid: '05a29f94-c0ed-11e2-94be-8c13b969e334',
551
+ identifierUuid: '1f0ad7a1-430f-4397-b571-59ea654a52db',
552
+ identifierValue: '100GEJ',
553
+ initialValue: '100GEJ',
554
+ preferred: true,
555
+ required: true,
556
+ selectedSource: null,
557
+ },
558
+ },
559
+ isDead: false,
560
+ middleName: '',
561
+ monthsEstimated: 0,
562
+ patientUuid: '8673ee4f-e2ab-4077-ba55-4980f408773e',
563
+ relationships: [],
564
+ telephoneNumber: '',
565
+ unidentifiedPatient: undefined,
566
+ yearsEstimated: 0,
567
+ },
568
+ expect.anything(),
569
+ expect.anything(),
570
+ null,
571
+ undefined,
572
+ expect.anything(),
573
+ expect.anything(),
574
+ expect.anything(),
575
+ { patientSaved: false },
576
+ expect.anything(),
577
+ );
578
+ });
579
+ });
@@ -24,6 +24,7 @@ export interface FetchedPatientIdentifierType {
24
24
  uuid: string;
25
25
  fieldName: string;
26
26
  format: string;
27
+ formatDescription?: string;
27
28
  isPrimary: boolean;
28
29
  /** See: https://github.com/openmrs/openmrs-core/blob/e3fb1ac0a052aeff0f957a150731757dd319693b/api/src/main/java/org/openmrs/PatientIdentifierType.java#L41 */
29
30
  uniquenessBehavior: undefined | null | 'UNIQUE' | 'NON_UNIQUE' | 'LOCATION';
@@ -118,7 +119,7 @@ export type Patient = {
118
119
  };
119
120
 
120
121
  export interface Encounter {
121
- encounterDatetime?: Date;
122
+ encounterDatetime: Date;
122
123
  patient: string;
123
124
  encounterType: string;
124
125
  location: string;
@@ -154,39 +155,39 @@ export interface RelationshipValue {
154
155
  }
155
156
 
156
157
  export interface FormValues {
157
- patientUuid: string;
158
- givenName: string;
159
- middleName: string;
160
- familyName: string;
158
+ additionalFamilyName: string;
161
159
  additionalGivenName: string;
162
160
  additionalMiddleName: string;
163
- additionalFamilyName: string;
164
161
  addNameInLocalLanguage: boolean;
165
- gender: string;
162
+ address: {
163
+ [addressField: string]: string;
164
+ };
165
+ attributes?: {
166
+ [attributeTypeUuid: string]: string;
167
+ };
166
168
  birthdate: Date | string;
167
- yearsEstimated: number;
168
- monthsEstimated: number;
169
169
  birthdateEstimated: boolean;
170
- telephoneNumber: string;
171
- isDead: boolean;
172
- deathDate: string;
173
170
  deathCause: string;
174
- relationships: Array<RelationshipValue>;
171
+ deathDate: string | Date;
172
+ deathTime: string;
173
+ deathTimeFormat: 'AM' | 'PM';
174
+ familyName: string;
175
+ gender: string;
176
+ givenName: string;
175
177
  identifiers: {
176
178
  [identifierFieldName: string]: PatientIdentifierValue;
177
179
  };
178
- attributes?: {
179
- [attributeTypeUuid: string]: string;
180
- };
180
+ isDead: boolean;
181
+ middleName: string;
182
+ monthsEstimated: number;
183
+ nonCodedCauseOfDeath: string;
181
184
  obs?: {
182
185
  [conceptUuid: string]: string;
183
186
  };
184
- address: {
185
- [addressField: string]: string;
186
- };
187
- observation?: ObsResponse;
188
- concepts?: Array<ConceptAnswers>;
189
- token?: string;
187
+ patientUuid: string;
188
+ relationships: Array<RelationshipValue>;
189
+ telephoneNumber: string;
190
+ yearsEstimated: number;
190
191
  }
191
192
 
192
193
  export interface PatientUuidMapType {
@@ -319,6 +320,3 @@ export interface RestAddressTemplate {
319
320
  display: string;
320
321
  value: string;
321
322
  }
322
- export interface ObsResponse {
323
- results: Array<{ obs: Array<{ uuid: string; display: string; value: OpenmrsResource; concept: OpenmrsResource }> }>;
324
- }