@olaboot/esm-patient-registration-app 9.2.0 → 10.0.2

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 (346) hide show
  1. package/dist/1339.js +1 -0
  2. package/dist/1339.js.map +1 -0
  3. package/dist/1480.js +1 -0
  4. package/dist/1480.js.map +1 -0
  5. package/dist/1646.js +1 -0
  6. package/dist/1646.js.map +1 -0
  7. package/dist/1789.js +1 -0
  8. package/dist/1789.js.map +1 -0
  9. package/dist/1869.js +1 -0
  10. package/dist/1869.js.map +1 -0
  11. package/dist/1877.js +1 -0
  12. package/dist/1877.js.map +1 -0
  13. package/dist/2317.js +1 -0
  14. package/dist/2317.js.map +1 -0
  15. package/dist/2416.js +1 -0
  16. package/dist/2416.js.map +1 -0
  17. package/dist/2747.js +1 -0
  18. package/dist/2747.js.map +1 -0
  19. package/dist/282.js +1 -0
  20. package/dist/282.js.map +1 -0
  21. package/dist/2881.js +1 -0
  22. package/dist/2881.js.map +1 -0
  23. package/dist/3378.js +1 -0
  24. package/dist/3378.js.map +1 -0
  25. package/dist/3720.js +1 -0
  26. package/dist/3720.js.map +1 -0
  27. package/dist/3906.js +1 -0
  28. package/dist/3906.js.map +1 -0
  29. package/dist/3963.js +1 -0
  30. package/dist/3963.js.map +1 -0
  31. package/dist/3989.js +1 -0
  32. package/dist/3989.js.map +1 -0
  33. package/dist/4106.js +1 -0
  34. package/dist/4106.js.map +1 -0
  35. package/dist/4111.js +1 -0
  36. package/dist/4111.js.map +1 -0
  37. package/dist/434.js +1 -0
  38. package/dist/434.js.map +1 -0
  39. package/dist/4348.js +1 -0
  40. package/dist/4348.js.map +1 -0
  41. package/dist/4383.js +1 -0
  42. package/dist/4383.js.map +1 -0
  43. package/dist/4658.js +1 -0
  44. package/dist/4658.js.map +1 -0
  45. package/dist/466.js +1 -0
  46. package/dist/466.js.map +1 -0
  47. package/dist/4928.js +1 -0
  48. package/dist/4928.js.map +1 -0
  49. package/dist/5117.js +1 -0
  50. package/dist/5117.js.map +1 -0
  51. package/dist/5132.js +1 -0
  52. package/dist/5132.js.map +1 -0
  53. package/dist/5145.js +1 -0
  54. package/dist/5145.js.map +1 -0
  55. package/dist/5208.js +43 -0
  56. package/dist/5208.js.map +1 -0
  57. package/dist/527.js +1 -0
  58. package/dist/527.js.map +1 -0
  59. package/dist/5280.js +1 -0
  60. package/dist/5280.js.map +1 -0
  61. package/dist/5338.js +6 -0
  62. package/dist/5338.js.map +1 -0
  63. package/dist/5503.js +1 -0
  64. package/dist/5503.js.map +1 -0
  65. package/dist/555.js +1 -0
  66. package/dist/555.js.map +1 -0
  67. package/dist/556.js +1 -0
  68. package/dist/556.js.map +1 -0
  69. package/dist/5644.js +1 -0
  70. package/dist/5644.js.map +1 -0
  71. package/dist/5697.js +1 -0
  72. package/dist/{4024.js.map → 5697.js.map} +1 -1
  73. package/dist/5940.js +1 -0
  74. package/dist/5940.js.map +1 -0
  75. package/dist/6047.js +1 -0
  76. package/dist/6047.js.map +1 -0
  77. package/dist/6371.js +1 -0
  78. package/dist/6371.js.map +1 -0
  79. package/dist/6377.js +1 -0
  80. package/dist/6377.js.map +1 -0
  81. package/dist/6388.js +1 -0
  82. package/dist/6388.js.map +1 -0
  83. package/dist/6444.js +1 -0
  84. package/dist/6444.js.map +1 -0
  85. package/dist/6508.js +1 -0
  86. package/dist/6508.js.map +1 -0
  87. package/dist/6724.js +1 -0
  88. package/dist/6724.js.map +1 -0
  89. package/dist/689.js +1 -0
  90. package/dist/689.js.map +1 -0
  91. package/dist/6904.js +1 -0
  92. package/dist/6904.js.map +1 -0
  93. package/dist/7045.js +1 -0
  94. package/dist/7045.js.map +1 -0
  95. package/dist/7175.js +1 -0
  96. package/dist/7175.js.map +1 -0
  97. package/dist/7182.js +1 -0
  98. package/dist/7182.js.map +1 -0
  99. package/dist/7649.js +1 -0
  100. package/dist/7649.js.map +1 -0
  101. package/dist/7742.js +1 -0
  102. package/dist/7742.js.map +1 -0
  103. package/dist/7912.js +1 -0
  104. package/dist/7912.js.map +1 -0
  105. package/dist/8358.js +1 -0
  106. package/dist/8358.js.map +1 -0
  107. package/dist/8359.js +1 -0
  108. package/dist/8359.js.map +1 -0
  109. package/dist/8695.js +1 -0
  110. package/dist/8695.js.map +1 -0
  111. package/dist/903.js +1 -0
  112. package/dist/903.js.map +1 -0
  113. package/dist/9061.js +1 -0
  114. package/dist/9061.js.map +1 -0
  115. package/dist/9072.js +1 -0
  116. package/dist/9072.js.map +1 -0
  117. package/dist/9397.js +1 -0
  118. package/dist/9397.js.map +1 -0
  119. package/dist/9712.js +1 -0
  120. package/dist/9712.js.map +1 -0
  121. package/dist/9771.js +1 -0
  122. package/dist/9771.js.map +1 -0
  123. package/dist/9806.js +1 -0
  124. package/dist/9806.js.map +1 -0
  125. package/dist/9816.js +1 -0
  126. package/dist/9816.js.map +1 -0
  127. package/dist/main.js +7 -6
  128. package/dist/main.js.map +1 -1
  129. package/dist/openmrs-esm-patient-registration-app.js +6 -0
  130. package/dist/{olaboot-esm-patient-registration-app.js.buildmanifest.json → openmrs-esm-patient-registration-app.js.buildmanifest.json} +540 -455
  131. package/dist/openmrs-esm-patient-registration-app.js.map +1 -0
  132. package/dist/routes.json +1 -1
  133. package/package.json +8 -9
  134. package/src/add-patient-link.extension.tsx +3 -2
  135. package/src/add-patient-link.test.tsx +2 -1
  136. package/src/config-schema.ts +1 -1
  137. package/src/index.ts +2 -24
  138. package/src/nav-link.test.tsx +1 -0
  139. package/src/offline.resources.ts +97 -31
  140. package/src/patient-registration/before-save-prompt.test.tsx +199 -0
  141. package/src/patient-registration/field/__mocks__/field.resource.ts +8 -7
  142. package/src/patient-registration/field/address/address-field.component.tsx +10 -13
  143. package/src/patient-registration/field/address/address-hierarchy-levels.component.tsx +6 -1
  144. package/src/patient-registration/field/address/address-hierarchy.test.tsx +191 -198
  145. package/src/patient-registration/field/address/address-search.component.tsx +20 -8
  146. package/src/patient-registration/field/address/address-search.scss +19 -2
  147. package/src/patient-registration/field/address/address-search.test.tsx +249 -57
  148. package/src/patient-registration/field/address/custom-address-field.component.tsx +1 -1
  149. package/src/patient-registration/field/cause-of-death/cause-of-death.component.tsx +1 -1
  150. package/src/patient-registration/field/cause-of-death/cause-of-death.test.tsx +251 -0
  151. package/src/patient-registration/field/custom-field.component.tsx +1 -1
  152. package/src/patient-registration/field/date-and-time-of-death/date-and-time-of-death.component.tsx +1 -1
  153. package/src/patient-registration/field/date-and-time-of-death/date-and-time-of-death.test.tsx +144 -0
  154. package/src/patient-registration/field/dob/dob.component.tsx +2 -2
  155. package/src/patient-registration/field/dob/dob.test.tsx +370 -54
  156. package/src/patient-registration/field/field.component.tsx +1 -1
  157. package/src/patient-registration/field/field.resource.ts +2 -2
  158. package/src/patient-registration/field/field.test.tsx +25 -22
  159. package/src/patient-registration/field/gender/gender-field.test.tsx +240 -54
  160. package/src/patient-registration/field/id/id-field.component.tsx +15 -5
  161. package/src/patient-registration/field/id/id-field.test.tsx +103 -47
  162. package/src/patient-registration/field/id/identifier-selection-overlay.test.tsx +346 -0
  163. package/src/patient-registration/field/name/name-field.component.tsx +2 -2
  164. package/src/patient-registration/field/name/name-field.test.tsx +282 -0
  165. package/src/patient-registration/field/obs/obs-field.test.tsx +294 -118
  166. package/src/patient-registration/field/person-attributes/coded-person-attribute-field.test.tsx +172 -108
  167. package/src/patient-registration/field/person-attributes/location-person-attribute-field.component.tsx +3 -3
  168. package/src/patient-registration/field/person-attributes/location-person-attribute-field.resource.tsx +2 -5
  169. package/src/patient-registration/field/person-attributes/person-attribute-field.component.tsx +2 -2
  170. package/src/patient-registration/field/person-attributes/person-attribute-field.test.tsx +249 -131
  171. package/src/patient-registration/field/person-attributes/person-attributes.resource.ts +1 -1
  172. package/src/patient-registration/field/person-attributes/text-person-attribute-field.test.tsx +98 -70
  173. package/src/patient-registration/field/phone/phone-field.test.tsx +100 -0
  174. package/src/patient-registration/form-manager.test.ts +6 -5
  175. package/src/patient-registration/form-manager.ts +5 -2
  176. package/src/patient-registration/input/basic-input/input/input.component.tsx +3 -121
  177. package/src/patient-registration/input/basic-input/input/input.test.tsx +151 -51
  178. package/src/patient-registration/input/basic-input/select/select-input.test.tsx +113 -33
  179. package/src/patient-registration/input/combo-input/combo-input.component.tsx +60 -24
  180. package/src/patient-registration/input/custom-input/autosuggest/autosuggest.component.tsx +10 -101
  181. package/src/patient-registration/input/custom-input/autosuggest/autosuggest.test.tsx +144 -108
  182. package/src/patient-registration/input/custom-input/identifier/identifier-input.test.tsx +241 -177
  183. package/src/patient-registration/input/custom-input/identifier/utils.test.ts +47 -8
  184. package/src/patient-registration/input/dummy-data/dummy-data-input.component.tsx +12 -12
  185. package/src/patient-registration/input/dummy-data/dummy-data-input.test.tsx +52 -20
  186. package/src/patient-registration/input/input.scss +1 -2
  187. package/src/patient-registration/patient-registration-context.ts +5 -3
  188. package/src/patient-registration/patient-registration-hooks.ts +4 -12
  189. package/src/patient-registration/patient-registration-utils.test.ts +2 -1
  190. package/src/patient-registration/patient-registration-utils.ts +2 -98
  191. package/src/patient-registration/patient-registration.component.tsx +50 -46
  192. package/src/patient-registration/patient-registration.resource.test.tsx +4 -7
  193. package/src/patient-registration/patient-registration.resource.ts +1 -4
  194. package/src/patient-registration/patient-registration.scss +16 -3
  195. package/src/patient-registration/patient-registration.test.tsx +99 -65
  196. package/src/patient-registration/patient-registration.types.ts +17 -28
  197. package/src/patient-registration/section/death-info/death-info-section.test.tsx +130 -34
  198. package/src/patient-registration/section/demographics/demographics-section.test.tsx +122 -68
  199. package/src/patient-registration/section/patient-relationships/relationships-section.component.tsx +15 -15
  200. package/src/patient-registration/section/patient-relationships/relationships-section.test.tsx +278 -84
  201. package/src/patient-registration/section/section-wrapper.component.tsx +1 -1
  202. package/src/patient-registration/ui-components/overlay/overlay.test.tsx +104 -0
  203. package/src/patient-registration/validation/patient-registration-validation.test.ts +2 -1
  204. package/src/patient-registration/validation/patient-registration-validation.ts +9 -3
  205. package/src/root.component.tsx +2 -5
  206. package/src/widgets/cancel-patient-edit.test.tsx +48 -11
  207. package/src/widgets/delete-identifier-confirmation.test.tsx +77 -24
  208. package/src/widgets/edit-patient-details-button.component.tsx +14 -18
  209. package/src/widgets/edit-patient-details-button.scss +2 -2
  210. package/src/widgets/edit-patient-details-button.test.tsx +11 -13
  211. package/translations/am.json +9 -4
  212. package/translations/ar.json +9 -4
  213. package/translations/ar_SY.json +9 -4
  214. package/translations/bn.json +9 -4
  215. package/translations/cs.json +9 -4
  216. package/translations/de.json +120 -115
  217. package/translations/en.json +9 -4
  218. package/translations/en_US.json +9 -4
  219. package/translations/es.json +9 -4
  220. package/translations/es_MX.json +9 -4
  221. package/translations/fr.json +9 -4
  222. package/translations/he.json +9 -4
  223. package/translations/hi.json +9 -4
  224. package/translations/hi_IN.json +9 -4
  225. package/translations/id.json +9 -4
  226. package/translations/it.json +9 -4
  227. package/translations/ka.json +9 -4
  228. package/translations/km.json +9 -4
  229. package/translations/ku.json +9 -4
  230. package/translations/ky.json +9 -4
  231. package/translations/lg.json +9 -4
  232. package/translations/ne.json +9 -4
  233. package/translations/pl.json +9 -4
  234. package/translations/pt.json +9 -4
  235. package/translations/pt_BR.json +10 -5
  236. package/translations/qu.json +9 -4
  237. package/translations/ro_RO.json +9 -4
  238. package/translations/ru_RU.json +9 -4
  239. package/translations/si.json +9 -4
  240. package/translations/sq.json +9 -4
  241. package/translations/sw.json +9 -4
  242. package/translations/sw_KE.json +9 -4
  243. package/translations/tr.json +9 -4
  244. package/translations/tr_TR.json +9 -4
  245. package/translations/uk.json +9 -4
  246. package/translations/uz.json +9 -4
  247. package/translations/uz@Latn.json +9 -4
  248. package/translations/uz_UZ.json +9 -4
  249. package/translations/vi.json +9 -4
  250. package/translations/zh.json +50 -45
  251. package/translations/zh_CN.json +9 -4
  252. package/translations/zh_TW.json +9 -4
  253. package/vitest.config.ts +4 -0
  254. package/ADDRESS_CONFIGURATION.md +0 -152
  255. package/IDENTIFIER_CONFIGURATION.md +0 -142
  256. package/IMPLEMENTATION_SUMMARY.md +0 -111
  257. package/QUICK_START.md +0 -95
  258. package/address-required-fields-config.json +0 -26
  259. package/dist/126.js +0 -1
  260. package/dist/15.js +0 -1
  261. package/dist/1564.js +0 -1
  262. package/dist/1567.js +0 -1
  263. package/dist/1845.js +0 -1
  264. package/dist/1953.js +0 -1
  265. package/dist/200.js +0 -1
  266. package/dist/200.js.map +0 -1
  267. package/dist/215.js +0 -1
  268. package/dist/2178.js +0 -1
  269. package/dist/250.js +0 -1
  270. package/dist/250.js.map +0 -1
  271. package/dist/2523.js +0 -1
  272. package/dist/2523.js.map +0 -1
  273. package/dist/2566.js +0 -1
  274. package/dist/2586.js +0 -1
  275. package/dist/2586.js.map +0 -1
  276. package/dist/2716.js +0 -1
  277. package/dist/2716.js.map +0 -1
  278. package/dist/2759.js +0 -1
  279. package/dist/2821.js +0 -6
  280. package/dist/2821.js.map +0 -1
  281. package/dist/3089.js +0 -1
  282. package/dist/3089.js.map +0 -1
  283. package/dist/3230.js +0 -1
  284. package/dist/3441.js +0 -1
  285. package/dist/3565.js +0 -1
  286. package/dist/3571.js +0 -1
  287. package/dist/3571.js.map +0 -1
  288. package/dist/3746.js +0 -1
  289. package/dist/3925.js +0 -1
  290. package/dist/3946.js +0 -1
  291. package/dist/4024.js +0 -1
  292. package/dist/4744.js +0 -1
  293. package/dist/4744.js.map +0 -1
  294. package/dist/4809.js +0 -1
  295. package/dist/4894.js +0 -1
  296. package/dist/4970.js +0 -1
  297. package/dist/4970.js.map +0 -1
  298. package/dist/5130.js +0 -1
  299. package/dist/5187.js +0 -1
  300. package/dist/5491.js +0 -1
  301. package/dist/5491.js.map +0 -1
  302. package/dist/5595.js +0 -1
  303. package/dist/5961.js +0 -1
  304. package/dist/6133.js +0 -1
  305. package/dist/634.js +0 -1
  306. package/dist/634.js.map +0 -1
  307. package/dist/6456.js +0 -1
  308. package/dist/6466.js +0 -1
  309. package/dist/6613.js +0 -1
  310. package/dist/6783.js +0 -1
  311. package/dist/7073.js +0 -38
  312. package/dist/7073.js.map +0 -1
  313. package/dist/7154.js +0 -1
  314. package/dist/7154.js.map +0 -1
  315. package/dist/7348.js +0 -1
  316. package/dist/7439.js +0 -1
  317. package/dist/7439.js.map +0 -1
  318. package/dist/7543.js +0 -1
  319. package/dist/7607.js +0 -1
  320. package/dist/772.js +0 -1
  321. package/dist/7984.js +0 -1
  322. package/dist/7984.js.map +0 -1
  323. package/dist/8538.js +0 -1
  324. package/dist/8538.js.map +0 -1
  325. package/dist/8599.js +0 -1
  326. package/dist/8727.js +0 -1
  327. package/dist/8847.js +0 -1
  328. package/dist/9015.js +0 -1
  329. package/dist/906.js +0 -1
  330. package/dist/9065.js +0 -1
  331. package/dist/9182.js +0 -1
  332. package/dist/9339.js +0 -1
  333. package/dist/9453.js +0 -1
  334. package/dist/9833.js +0 -1
  335. package/dist/9833.js.map +0 -1
  336. package/dist/9856.js +0 -1
  337. package/dist/9856.js.map +0 -1
  338. package/dist/9920.js +0 -1
  339. package/dist/9938.js +0 -1
  340. package/dist/9943.js +0 -1
  341. package/dist/9943.js.map +0 -1
  342. package/dist/olaboot-esm-patient-registration-app.js +0 -5
  343. package/dist/olaboot-esm-patient-registration-app.js.map +0 -1
  344. package/example-config.json +0 -14
  345. package/jest.config.js +0 -3
  346. package/src/resource.ts +0 -12
@@ -1,7 +1,14 @@
1
+ /**
2
+ * @vitest-environment jsdom
3
+ *
4
+ * The form-submit flow under test does not fire its callback under happy-dom
5
+ * (likely a DOM-event-dispatch divergence). Run this file under jsdom.
6
+ */
1
7
  import React from 'react';
8
+ import { vi, describe, it, expect, test, beforeEach } from 'vitest';
2
9
  import userEvent from '@testing-library/user-event';
3
10
  import { useParams } from 'react-router-dom';
4
- import { screen, within } from '@testing-library/react';
11
+ import { fireEvent, screen, within } from '@testing-library/react';
5
12
  import {
6
13
  type FetchResponse,
7
14
  getDefaultsFromConfigSchema,
@@ -19,18 +26,18 @@ import { PatientRegistration } from './patient-registration.component';
19
26
  import { useInitialFormValues } from './patient-registration-hooks';
20
27
  import { ResourcesContextProvider } from '../resources-context';
21
28
 
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
+ const mockSaveEncounter = vi.mocked(saveEncounter);
30
+ const mockSavePatient = vi.mocked(savePatient);
31
+ const mockShowSnackbar = vi.mocked(showSnackbar);
32
+ const mockUseConfig = vi.mocked(useConfig<RegistrationConfig>);
33
+ const mockUsePatient = vi.mocked(usePatient);
34
+ const mockUseParams = vi.mocked(useParams);
35
+ const mockUseInitialFormValues = vi.mocked(useInitialFormValues);
29
36
 
30
- jest.mock('./field/field.resource', () => ({
31
- useConcept: jest.fn().mockImplementation((uuid: string) => {
37
+ vi.mock('./field/field.resource', () => ({
38
+ useConcept: vi.fn().mockImplementation((uuid: string) => {
32
39
  let data;
33
- if (uuid == 'weight-uuid') {
40
+ if (uuid === 'weight-uuid') {
34
41
  data = {
35
42
  uuid: 'weight-uuid',
36
43
  display: 'Weight (kg)',
@@ -38,7 +45,7 @@ jest.mock('./field/field.resource', () => ({
38
45
  answers: [],
39
46
  setMembers: [],
40
47
  };
41
- } else if (uuid == 'chief-complaint-uuid') {
48
+ } else if (uuid === 'chief-complaint-uuid') {
42
49
  data = {
43
50
  uuid: 'chief-complaint-uuid',
44
51
  display: 'Chief Complaint',
@@ -46,7 +53,7 @@ jest.mock('./field/field.resource', () => ({
46
53
  answers: [],
47
54
  setMembers: [],
48
55
  };
49
- } else if (uuid == 'nationality-uuid') {
56
+ } else if (uuid === 'nationality-uuid') {
50
57
  data = {
51
58
  uuid: 'nationality-uuid',
52
59
  display: 'Nationality',
@@ -63,8 +70,8 @@ jest.mock('./field/field.resource', () => ({
63
70
  isLoading: !data,
64
71
  };
65
72
  }),
66
- useConceptAnswers: jest.fn().mockImplementation((uuid: string) => {
67
- if (uuid == 'nationality-uuid') {
73
+ useConceptAnswers: vi.fn().mockImplementation((uuid: string) => {
74
+ if (uuid === 'nationality-uuid') {
68
75
  return {
69
76
  data: [
70
77
  { display: 'USA', uuid: 'usa' },
@@ -72,7 +79,7 @@ jest.mock('./field/field.resource', () => ({
72
79
  ],
73
80
  isLoading: false,
74
81
  };
75
- } else if (uuid == 'other-countries-uuid') {
82
+ } else if (uuid === 'other-countries-uuid') {
76
83
  return {
77
84
  data: [
78
85
  { display: 'Kenya', uuid: 'ke' },
@@ -84,27 +91,56 @@ jest.mock('./field/field.resource', () => ({
84
91
  }),
85
92
  }));
86
93
 
87
- jest.mock('react-router-dom', () => ({
88
- ...jest.requireActual('react-router-dom'),
94
+ vi.mock('react-router-dom', async () => ({
95
+ ...((await vi.importActual('react-router-dom')) as object),
89
96
  useLocation: () => ({
90
97
  pathname: 'openmrs/spa/patient-registration',
91
98
  }),
92
99
  useHistory: () => [],
93
- useParams: jest.fn().mockReturnValue({ patientUuid: undefined }),
100
+ useParams: vi.fn().mockReturnValue({ patientUuid: undefined }),
94
101
  }));
95
102
 
96
- jest.mock('./patient-registration.resource', () => ({
97
- ...jest.requireActual('./patient-registration.resource'),
98
- saveEncounter: jest.fn(),
99
- savePatient: jest.fn(),
103
+ vi.mock('./patient-registration.resource', async () => ({
104
+ ...((await vi.importActual('./patient-registration.resource')) as object),
105
+ saveEncounter: vi.fn(),
106
+ savePatient: vi.fn(),
100
107
  }));
101
108
 
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
- }));
109
+ vi.mock('./patient-registration-hooks', async () => {
110
+ const actual = (await vi.importActual('./patient-registration-hooks')) as object;
111
+ const defaultInitialFormValues = {
112
+ additionalFamilyName: '',
113
+ additionalGivenName: '',
114
+ additionalMiddleName: '',
115
+ addNameInLocalLanguage: false,
116
+ address: {},
117
+ birthdate: '',
118
+ birthdateEstimated: false,
119
+ deathCause: '',
120
+ deathDate: undefined,
121
+ deathTime: undefined,
122
+ deathTimeFormat: 'AM',
123
+ familyName: '',
124
+ gender: '',
125
+ givenName: '',
126
+ identifiers: {},
127
+ isDead: false,
128
+ middleName: '',
129
+ monthsEstimated: 0,
130
+ nonCodedCauseOfDeath: '',
131
+ patientUuid: 'new-patient-uuid',
132
+ relationships: [],
133
+ telephoneNumber: '',
134
+ yearsEstimated: 0,
135
+ } as FormValues;
136
+
137
+ return {
138
+ ...actual,
139
+ useInitialFormValues: vi.fn().mockReturnValue([defaultInitialFormValues, vi.fn()]),
140
+ useInitialAddressFieldValues: vi.fn().mockReturnValue([{}, vi.fn()]),
141
+ usePatientUuidMap: vi.fn().mockReturnValue([{}, vi.fn()]),
142
+ };
143
+ });
108
144
 
109
145
  const mockResourcesContextValue = {
110
146
  addressTemplate: mockedAddressTemplate as AddressTemplate,
@@ -113,7 +149,7 @@ const mockResourcesContextValue = {
113
149
  sessionId: 'JSESSION',
114
150
  currentProvider: { uuid: 'provider-uuid', identifier: 'PRO-123' },
115
151
  },
116
- relationshipTypes: [],
152
+ relationshipTypes: { results: [] },
117
153
  identifierTypes: [],
118
154
  };
119
155
 
@@ -161,7 +197,6 @@ const mockOpenmrsConfig: RegistrationConfig = {
161
197
  useQuickSearch: true,
162
198
  searchAddressByLevel: true,
163
199
  },
164
- requiredAddressFields: [],
165
200
  },
166
201
  causeOfDeath: {
167
202
  conceptUuid: 'cause-of-death-concept-uuid',
@@ -226,24 +261,26 @@ const fillRequiredFields = async () => {
226
261
  const demographicsSection = await screen.findByLabelText('Demographics Section');
227
262
  const givenNameInput = within(demographicsSection).getByLabelText(/first/i) as HTMLInputElement;
228
263
  const familyNameInput = within(demographicsSection).getByLabelText(/family/i) as HTMLInputElement;
229
- const dateInput = within(demographicsSection).getByRole('spinbutton', {
230
- name: /day, date of birth/i,
231
- }) as HTMLInputElement;
232
- const monthInput = within(demographicsSection).getByRole('spinbutton', {
233
- name: /month, date of birth/i,
234
- }) as HTMLInputElement;
235
- const yearInput = within(demographicsSection).getByRole('spinbutton', {
236
- name: /year, date of birth/i,
237
- }) as HTMLInputElement;
238
- const genderInput = within(demographicsSection).getByLabelText(/Male/) as HTMLSelectElement;
264
+
239
265
  await user.type(givenNameInput, 'Paul');
240
266
  await user.type(familyNameInput, 'Gaihre');
241
- await user.clear(dateInput);
242
- await user.type(dateInput, '02');
243
- await user.clear(monthInput);
244
- await user.type(monthInput, '08');
245
- await user.clear(yearInput);
246
- await user.type(yearInput, '1993');
267
+
268
+ // Set birthdate using the date picker's public API
269
+ let dateOfBirthInput: HTMLInputElement;
270
+ const birthDateString = '1993-08-02';
271
+
272
+ try {
273
+ // Approach 1: Find as textbox (like appointments test)
274
+ dateOfBirthInput = within(demographicsSection).getByRole('textbox', { name: /date of birth/i }) as HTMLInputElement;
275
+ } catch (e) {
276
+ // Approach 2: Find by label
277
+ dateOfBirthInput = within(demographicsSection).getByLabelText(/date of birth/i) as HTMLInputElement;
278
+ }
279
+
280
+ fireEvent.change(dateOfBirthInput, { target: { value: birthDateString } });
281
+ fireEvent.blur(dateOfBirthInput);
282
+
283
+ const genderInput = within(demographicsSection).getByLabelText(/Male/) as HTMLSelectElement;
247
284
  await user.click(genderInput);
248
285
  };
249
286
 
@@ -253,12 +290,12 @@ describe('Registering a new patient', () => {
253
290
  ...getDefaultsFromConfigSchema(esmPatientRegistrationSchema),
254
291
  ...mockOpenmrsConfig,
255
292
  });
256
- mockSavePatient.mockReturnValue({ data: { uuid: 'new-pt-uuid' }, ok: true });
293
+ mockSavePatient.mockResolvedValue({ data: { uuid: 'new-pt-uuid' }, ok: true } as any);
257
294
  });
258
295
 
259
296
  it('should render all the required fields and sections', async () => {
260
297
  renderWithContext(
261
- <PatientRegistration isOffline={false} savePatientForm={jest.fn()} />,
298
+ <PatientRegistration isOffline={false} savePatientForm={vi.fn()} />,
262
299
  ResourcesContextProvider,
263
300
  mockResourcesContextValue,
264
301
  );
@@ -286,8 +323,7 @@ describe('Registering a new patient', () => {
286
323
  expect(screen.getByRole('button', { name: /cancel/i })).toBeInTheDocument();
287
324
  });
288
325
 
289
- // FIXME the register patient button is missing
290
- it.skip('saves the patient without extra info', async () => {
326
+ it('saves the patient without extra info', async () => {
291
327
  const user = userEvent.setup();
292
328
 
293
329
  renderWithContext(
@@ -297,10 +333,11 @@ describe('Registering a new patient', () => {
297
333
  );
298
334
 
299
335
  await fillRequiredFields();
300
- await user.click(await screen.findByText(/Register Patient/i));
336
+ const registerButton = await screen.findByRole('button', { name: /register patient/i });
337
+ await user.click(registerButton);
301
338
  expect(mockSavePatient).toHaveBeenCalledWith(
302
339
  expect.objectContaining({
303
- identifiers: [], //TODO when the identifer story is finished: { identifier: '', identifierType: '05a29f94-c0ed-11e2-94be-8c13b969e334', location: '' },
340
+ identifiers: [], // Note: Identifier story may need test data update when complete
304
341
  person: {
305
342
  addresses: expect.arrayContaining([expect.any(Object)]),
306
343
  attributes: [],
@@ -319,7 +356,7 @@ describe('Registering a new patient', () => {
319
356
 
320
357
  it('should not save the patient if validation fails', async () => {
321
358
  const user = userEvent.setup();
322
- const mockSavePatientForm = jest.fn();
359
+ const mockSavePatientForm = vi.fn();
323
360
 
324
361
  renderWithContext(
325
362
  <PatientRegistration isOffline={false} savePatientForm={mockSavePatientForm} />,
@@ -333,8 +370,7 @@ describe('Registering a new patient', () => {
333
370
  expect(mockSavePatientForm).not.toHaveBeenCalled();
334
371
  });
335
372
 
336
- // FIXME: the register patient button is missing
337
- it.skip('renders and saves registration obs', async () => {
373
+ it('renders and saves registration obs', async () => {
338
374
  const user = userEvent.setup();
339
375
 
340
376
  mockSaveEncounter.mockResolvedValue({} as unknown as FetchResponse);
@@ -355,7 +391,8 @@ describe('Registering a new patient', () => {
355
391
  const nationality = within(customSection).getByLabelText('Nationality');
356
392
  await user.selectOptions(nationality, 'USA');
357
393
 
358
- await user.click(screen.getByText(/Register Patient/i));
394
+ const registerButton = await screen.findByRole('button', { name: /register patient/i });
395
+ await user.click(registerButton);
359
396
 
360
397
  expect(mockSavePatient).toHaveBeenCalled();
361
398
 
@@ -372,8 +409,7 @@ describe('Registering a new patient', () => {
372
409
  );
373
410
  });
374
411
 
375
- // FIXME register patient button is missing
376
- it.skip('retries saving registration obs after a failed attempt', async () => {
412
+ it('retries saving registration obs after a failed attempt', async () => {
377
413
  const user = userEvent.setup();
378
414
 
379
415
  mockUseConfig.mockReturnValue(configWithObs);
@@ -391,7 +427,7 @@ describe('Registering a new patient', () => {
391
427
 
392
428
  mockSaveEncounter.mockRejectedValue({ status: 400, responseBody: { error: { message: 'an error message' } } });
393
429
 
394
- const registerPatientButton = screen.getByText(/Register Patient/i);
430
+ const registerPatientButton = await screen.findByRole('button', { name: /register patient/i });
395
431
 
396
432
  await user.click(registerPatientButton);
397
433
 
@@ -423,13 +459,13 @@ describe('Updating an existing patient record', () => {
423
459
  patientUuid: mockPatient.id,
424
460
  };
425
461
  });
426
- mockSavePatient.mockReturnValue({ data: { uuid: 'new-pt-uuid' }, ok: true });
462
+ mockSavePatient.mockResolvedValue({ data: { uuid: 'new-pt-uuid' }, ok: true } as any);
427
463
  mockUseParams.mockReturnValue({ patientUuid: mockPatient.id });
428
464
  });
429
465
 
430
466
  it('edits patient demographics', async () => {
431
467
  const user = userEvent.setup();
432
- const mockSavePatientForm = jest.fn();
468
+ const mockSavePatientForm = vi.fn();
433
469
 
434
470
  mockUseInitialFormValues.mockReturnValue([
435
471
  {
@@ -480,7 +516,7 @@ describe('Updating an existing patient record', () => {
480
516
  telephoneNumber: '',
481
517
  yearsEstimated: 0,
482
518
  } as FormValues,
483
- jest.fn(),
519
+ vi.fn(),
484
520
  ]);
485
521
 
486
522
  renderWithContext(
@@ -497,8 +533,6 @@ describe('Updating an existing patient record', () => {
497
533
 
498
534
  expect(screen.getByLabelText(/first name/i)).toHaveValue(mockPatient.name[0].given[0]);
499
535
  expect(screen.getByLabelText(/family name/i)).toHaveValue(mockPatient.name[0].family);
500
- // FIXME: Fix the mock so that this value is visible
501
- // expect(screen.getByLabelText(/date of birth/i)).toHaveValue(mockPatient.birthDate);
502
536
  expect(
503
537
  screen.getByRole('radio', {
504
538
  name: /^male$/i,
@@ -85,7 +85,7 @@ export interface PatientRegistration {
85
85
  isNewPatient: boolean;
86
86
  formValues: FormValues;
87
87
  patientUuidMap: PatientUuidMapType;
88
- initialAddressFieldValues: Record<string, any>;
88
+ initialAddressFieldValues: Partial<Record<AddressProperties, string>>;
89
89
  capturePhotoProps: CapturePhotoProps;
90
90
  currentLocation: string;
91
91
  initialIdentifierValues: FormValues['identifiers'];
@@ -201,22 +201,6 @@ export interface CapturePhotoProps {
201
201
  dateTime: string;
202
202
  }
203
203
 
204
- export interface AddressValidationSchemaType {
205
- name: string;
206
- label: string;
207
- regex: RegExp;
208
- regexFormat: string;
209
- }
210
-
211
- export interface CodedPersonAttributeConfig {
212
- personAttributeUuid: string;
213
- conceptUuid: string;
214
- }
215
-
216
- export interface TextBasedPersonAttributeConfig {
217
- personAttributeUuid: string;
218
- validationRegex: string;
219
- }
220
204
  export interface PatientIdentifierResponse {
221
205
  uuid: string;
222
206
  identifier: string;
@@ -251,6 +235,11 @@ export interface PersonAttributeResponse {
251
235
  };
252
236
  }
253
237
 
238
+ export interface ConceptAnswers {
239
+ display: string;
240
+ uuid: string;
241
+ }
242
+
254
243
  export interface ConceptResponse {
255
244
  uuid: string;
256
245
  display: string;
@@ -262,9 +251,18 @@ export interface ConceptResponse {
262
251
  setMembers: Array<ConceptAnswers>;
263
252
  }
264
253
 
265
- export interface ConceptAnswers {
266
- display: string;
254
+ export interface RelationshipType {
267
255
  uuid: string;
256
+ display: string;
257
+ aIsToB: string;
258
+ bIsToA: string;
259
+ displayAIsToB?: string;
260
+ displayBIsToA?: string;
261
+ description?: string;
262
+ }
263
+
264
+ export interface RelationshipTypesResponse {
265
+ results: Array<RelationshipType>;
268
266
  }
269
267
 
270
268
  export type AddressProperties =
@@ -311,12 +309,3 @@ export interface AddressTemplate {
311
309
  elementRegexFormats: ExtensibleAddressProperties;
312
310
  requiredElements: Array<AddressProperties> | null;
313
311
  }
314
-
315
- // https://rest.openmrs.org/#address-template
316
- export interface RestAddressTemplate {
317
- uuid: string;
318
- description: string;
319
- property: string;
320
- display: string;
321
- value: string;
322
- }
@@ -1,47 +1,143 @@
1
1
  import React from 'react';
2
- import { render, screen } from '@testing-library/react';
2
+ import { vi, describe, it, expect, beforeEach } from 'vitest';
3
+ import userEvent from '@testing-library/user-event';
4
+ import { render, screen, waitFor } from '@testing-library/react';
3
5
  import { Form, Formik } from 'formik';
6
+ import { getDefaultsFromConfigSchema, useConfig } from '@openmrs/esm-framework';
4
7
  import { type FormValues } from '../../patient-registration.types';
5
- import { renderWithContext } from 'tools';
6
8
  import { initialFormValues } from '../../patient-registration.component';
7
9
  import { PatientRegistrationContextProvider } from '../../patient-registration-context';
10
+ import { type RegistrationConfig, esmPatientRegistrationSchema } from '../../../config-schema';
8
11
  import { DeathInfoSection } from './death-info-section.component';
9
12
 
10
- const initialContextValues = {
11
- currentPhoto: 'data:image/png;base64,1234567890',
12
- identifierTypes: [],
13
- inEditMode: false,
14
- initialFormValues: {} as FormValues,
15
- isOffline: false,
16
- setCapturePhotoProps: jest.fn(),
17
- setFieldValue: jest.fn(),
18
- setFieldTouched: jest.fn(),
19
- setInitialFormValues: jest.fn(),
20
- validationSchema: null,
21
- values: {
22
- isDead: true,
23
- } as FormValues,
24
- };
13
+ const mockUseConfig = vi.mocked(useConfig<RegistrationConfig>);
25
14
 
26
- describe('Death info section', () => {
27
- const renderDeathInfoSection = (isDead) => {
28
- initialContextValues.values.isDead = isDead;
29
-
30
- renderWithContext(
31
- <Formik initialValues={initialFormValues} onSubmit={jest.fn()}>
32
- <Form>
33
- <DeathInfoSection fields={[]} />
34
- </Form>
35
- </Formik>,
36
- PatientRegistrationContextProvider,
37
- initialContextValues,
38
- );
15
+ /**
16
+ * Helper to render DeathInfoSection with Formik for state-dependent tests.
17
+ */
18
+ function renderDeathInfoSectionWithFormik(fields: string[] = [], initialValues: Partial<FormValues> = {}) {
19
+ const defaultValues = {
20
+ isDead: false,
21
+ ...initialValues,
39
22
  };
40
23
 
41
- it('shows fields for recording death info when the patient is marked as dead', () => {
42
- renderDeathInfoSection(true);
24
+ let formValuesRef: FormValues = { ...initialFormValues, ...defaultValues } as FormValues;
25
+
26
+ const utils = render(
27
+ <Formik initialValues={defaultValues} onSubmit={() => {}}>
28
+ {({ setFieldValue, values }) => {
29
+ formValuesRef = { ...initialFormValues, ...values } as FormValues;
30
+ return (
31
+ <Form>
32
+ <PatientRegistrationContextProvider
33
+ value={{
34
+ identifierTypes: [],
35
+ values: formValuesRef,
36
+ validationSchema: null,
37
+ inEditMode: false,
38
+ setFieldValue: setFieldValue as any,
39
+ setCapturePhotoProps: vi.fn(),
40
+ setFieldTouched: vi.fn().mockResolvedValue(undefined),
41
+ currentPhoto: '',
42
+ isOffline: false,
43
+ initialFormValues: formValuesRef,
44
+ }}>
45
+ <DeathInfoSection fields={fields} />
46
+ </PatientRegistrationContextProvider>
47
+ </Form>
48
+ );
49
+ }}
50
+ </Formik>,
51
+ );
52
+
53
+ return {
54
+ ...utils,
55
+ getFormValues: () => formValuesRef,
56
+ };
57
+ }
58
+
59
+ describe('Death info section', () => {
60
+ beforeEach(() => {
61
+ mockUseConfig.mockReturnValue({
62
+ ...getDefaultsFromConfigSchema(esmPatientRegistrationSchema),
63
+ fieldDefinitions: [],
64
+ });
65
+ });
66
+
67
+ describe('Rendering', () => {
68
+ it('renders the section with checkbox', () => {
69
+ renderDeathInfoSectionWithFormik();
70
+
71
+ expect(screen.getByRole('region', { name: /death info section/i })).toBeInTheDocument();
72
+ expect(screen.getByRole('checkbox', { name: /is dead/i })).toBeInTheDocument();
73
+ });
74
+
75
+ it('shows checkbox as checked when isDead is true', () => {
76
+ renderDeathInfoSectionWithFormik([], { isDead: true });
77
+
78
+ const checkbox = screen.getByRole('checkbox', { name: /is dead/i });
79
+ expect(checkbox).toBeChecked();
80
+ });
81
+
82
+ it('shows checkbox as unchecked when isDead is false', () => {
83
+ renderDeathInfoSectionWithFormik([], { isDead: false });
84
+
85
+ const checkbox = screen.getByRole('checkbox', { name: /is dead/i });
86
+ expect(checkbox).not.toBeChecked();
87
+ });
88
+ });
89
+
90
+ describe('User interaction', () => {
91
+ it('toggles death info fields when user clicks the checkbox', async () => {
92
+ const user = userEvent.setup();
93
+ const { getFormValues } = renderDeathInfoSectionWithFormik([]);
94
+
95
+ const checkbox = screen.getByRole('checkbox', { name: /is dead/i });
96
+ expect(checkbox).not.toBeChecked();
97
+
98
+ // Click to check
99
+ await user.click(checkbox);
100
+
101
+ await waitFor(() => {
102
+ expect(getFormValues().isDead).toBe(true);
103
+ });
104
+
105
+ await waitFor(() => {
106
+ expect(checkbox).toBeChecked();
107
+ });
108
+
109
+ // Click to uncheck
110
+ await user.click(checkbox);
111
+
112
+ await waitFor(() => {
113
+ expect(getFormValues().isDead).toBe(false);
114
+ });
115
+
116
+ await waitFor(() => {
117
+ expect(checkbox).not.toBeChecked();
118
+ });
119
+ });
120
+
121
+ it('updates form values when user toggles the checkbox', async () => {
122
+ const user = userEvent.setup();
123
+ const { getFormValues } = renderDeathInfoSectionWithFormik([]);
124
+
125
+ const checkbox = screen.getByRole('checkbox', { name: /is dead/i });
126
+ expect(checkbox).not.toBeChecked();
127
+ expect(getFormValues().isDead).toBe(false);
128
+
129
+ // Check the checkbox
130
+ await user.click(checkbox);
131
+
132
+ expect(checkbox).toBeChecked();
133
+ expect(getFormValues().isDead).toBe(true);
43
134
 
44
- expect(screen.getByRole('region', { name: /death info section/i })).toBeInTheDocument();
45
- expect(screen.getByRole('checkbox', { name: /is dead/i })).toBeInTheDocument();
135
+ // Uncheck the checkbox
136
+ await user.click(checkbox);
137
+ await waitFor(() => {
138
+ expect(checkbox).not.toBeChecked();
139
+ });
140
+ expect(getFormValues().isDead).toBe(false);
141
+ });
46
142
  });
47
143
  });