@openmrs/esm-form-engine-lib 2.1.0-pre.1435 → 2.1.0-pre.1442

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openmrs/esm-form-engine-lib",
3
- "version": "2.1.0-pre.1435",
3
+ "version": "2.1.0-pre.1442",
4
4
  "description": "React Form Engine for O3",
5
5
  "browser": "dist/openmrs-esm-form-engine-lib.js",
6
6
  "main": "src/index.ts",
@@ -0,0 +1,908 @@
1
+ import { type FormContextProps } from '../provider/form-provider';
2
+ import { type FormField } from '../types';
3
+ import { hasPreviousObsValueChanged, findObsByFormField, ObsAdapter } from './obs-adapter';
4
+
5
+ const formContext = {
6
+ methods: null,
7
+ workspaceLayout: 'maximized',
8
+ isSubmitting: false,
9
+ patient: {
10
+ id: '833db896-c1f0-11eb-8529-0242ac130003',
11
+ },
12
+ formJson: null,
13
+ visit: null,
14
+ sessionMode: 'enter',
15
+ sessionDate: new Date(),
16
+ location: {
17
+ uuid: '41e6e516-c1f0-11eb-8529-0242ac130003',
18
+ },
19
+ currentProvider: null,
20
+ layoutType: 'small-desktop',
21
+ domainObjectValue: {
22
+ uuid: '873455da-3ec4-453c-b565-7c1fe35426be',
23
+ obs: [],
24
+ },
25
+ previousDomainObjectValue: null,
26
+ processor: null,
27
+ formFields: [],
28
+ formFieldAdapters: null,
29
+ formFieldValidators: null,
30
+ customDependencies: null,
31
+ getFormField: jest.fn(),
32
+ addFormField: jest.fn(),
33
+ updateFormField: jest.fn(),
34
+ removeFormField: () => {},
35
+ addInvalidField: jest.fn(),
36
+ removeInvalidField: jest.fn(),
37
+ setInvalidFields: jest.fn(),
38
+ setForm: jest.fn(),
39
+ } as FormContextProps;
40
+
41
+ describe('ObsAdapter - transformFieldValue', () => {
42
+ // new submission (enter mode)
43
+ it('should handle submission for text input', () => {
44
+ // setup
45
+ const field: FormField = {
46
+ label: 'Visit note',
47
+ type: 'obs',
48
+ questionOptions: {
49
+ rendering: 'text',
50
+ concept: '1c43b05b-b6d8-4eb5-8f37-0b14f5347568',
51
+ },
52
+ id: 'visit-note',
53
+ };
54
+ // replay
55
+ const obs = ObsAdapter.transformFieldValue(field, 'Can be discharged in next visit', formContext);
56
+ // verify
57
+ expect(obs).toEqual({
58
+ concept: '1c43b05b-b6d8-4eb5-8f37-0b14f5347568',
59
+ formFieldNamespace: 'rfe-forms',
60
+ formFieldPath: 'rfe-forms-visit-note',
61
+ value: 'Can be discharged in next visit',
62
+ });
63
+ });
64
+
65
+ it('should handle submission for number input', () => {
66
+ // setup
67
+ const field: FormField = {
68
+ label: 'Temperature',
69
+ type: 'obs',
70
+ questionOptions: {
71
+ rendering: 'number',
72
+ concept: '2c43u05b-b6d8-4eju-8f37-0b14f5347560',
73
+ },
74
+ id: 'temperature',
75
+ };
76
+ // replay
77
+ const obs = ObsAdapter.transformFieldValue(field, 36, formContext);
78
+ // verify
79
+ expect(obs).toEqual({
80
+ concept: '2c43u05b-b6d8-4eju-8f37-0b14f5347560',
81
+ formFieldNamespace: 'rfe-forms',
82
+ formFieldPath: 'rfe-forms-temperature',
83
+ value: 36,
84
+ });
85
+ });
86
+
87
+ it('should handle submission for multiselect input', () => {
88
+ // setup
89
+ const field: FormField = {
90
+ label: 'Past enrolled patient programs',
91
+ type: 'obs',
92
+ questionOptions: {
93
+ rendering: 'checkbox',
94
+ concept: '3hbkj9-b6d8-4eju-8f37-0b14f5347jv9',
95
+ answers: [
96
+ { label: 'Oncology Screening and Diagnosis Program', concept: '105e7ad6-c1fd-11eb-8529-0242ac130ju9' },
97
+ { label: 'Fight Malaria Initiative', concept: '305e7ad6-c1fd-11eb-8529-0242ac130003' },
98
+ ],
99
+ },
100
+ id: 'past-patient-programs',
101
+ };
102
+
103
+ // replay
104
+ // Select Oncology Screening and Diagnosis Program
105
+ let obs = ObsAdapter.transformFieldValue(field, ['105e7ad6-c1fd-11eb-8529-0242ac130ju9'], formContext);
106
+
107
+ // verify
108
+ expect(obs).toEqual([
109
+ {
110
+ concept: '3hbkj9-b6d8-4eju-8f37-0b14f5347jv9',
111
+ formFieldNamespace: 'rfe-forms',
112
+ formFieldPath: 'rfe-forms-past-patient-programs',
113
+ value: '105e7ad6-c1fd-11eb-8529-0242ac130ju9',
114
+ },
115
+ ]);
116
+
117
+ // replay
118
+ // Add Fight Malaria Initiative
119
+ obs = ObsAdapter.transformFieldValue(
120
+ field,
121
+ ['105e7ad6-c1fd-11eb-8529-0242ac130ju9', '305e7ad6-c1fd-11eb-8529-0242ac130003'],
122
+ formContext,
123
+ );
124
+
125
+ // verify
126
+ expect(obs).toEqual([
127
+ {
128
+ concept: '3hbkj9-b6d8-4eju-8f37-0b14f5347jv9',
129
+ formFieldNamespace: 'rfe-forms',
130
+ formFieldPath: 'rfe-forms-past-patient-programs',
131
+ value: '105e7ad6-c1fd-11eb-8529-0242ac130ju9',
132
+ },
133
+ {
134
+ concept: '3hbkj9-b6d8-4eju-8f37-0b14f5347jv9',
135
+ formFieldNamespace: 'rfe-forms',
136
+ formFieldPath: 'rfe-forms-past-patient-programs',
137
+ value: '305e7ad6-c1fd-11eb-8529-0242ac130003',
138
+ },
139
+ ]);
140
+ });
141
+
142
+ it('should handle submission for date input', () => {
143
+ // setup
144
+ const field: FormField = {
145
+ label: 'HTS Date',
146
+ type: 'obs',
147
+ datePickerFormat: 'calendar',
148
+ questionOptions: {
149
+ rendering: 'date',
150
+ concept: 'j8b6705b-b6d8-4eju-8f37-0b14f5347569',
151
+ },
152
+ id: 'hts-date',
153
+ };
154
+ // Dec 20, 2019
155
+ const htsDate = new Date(2019, 11, 20);
156
+ // replay
157
+ const obs = ObsAdapter.transformFieldValue(field, htsDate, formContext);
158
+ // verify
159
+ expect(obs).toEqual({
160
+ concept: 'j8b6705b-b6d8-4eju-8f37-0b14f5347569',
161
+ formFieldNamespace: 'rfe-forms',
162
+ formFieldPath: 'rfe-forms-hts-date',
163
+ value: '2019-12-20',
164
+ });
165
+ });
166
+
167
+ it('should handle submission for single-select inputs', () => {
168
+ // setup
169
+ const field: FormField = {
170
+ label: 'HTS Result',
171
+ type: 'obs',
172
+ questionOptions: {
173
+ rendering: 'content-switcher',
174
+ concept: '89jbi9jk-b6d8-4eju-8f37-0b14f53mhj098b',
175
+ },
176
+ id: 'hts-result',
177
+ };
178
+ // replay
179
+ const obs = ObsAdapter.transformFieldValue(field, 'n8hynk0j-c1fd-117g-8529-0242ac1hgc9j', formContext);
180
+ // verify
181
+ expect(obs).toEqual({
182
+ concept: '89jbi9jk-b6d8-4eju-8f37-0b14f53mhj098b',
183
+ formFieldNamespace: 'rfe-forms',
184
+ formFieldPath: 'rfe-forms-hts-result',
185
+ value: 'n8hynk0j-c1fd-117g-8529-0242ac1hgc9j',
186
+ });
187
+ });
188
+
189
+ // editing existing values (edit mode)
190
+ it('should edit obs text/number value in edit mode', () => {
191
+ // setup
192
+ formContext.sessionMode = 'edit';
193
+ const field: FormField = {
194
+ label: 'Visit note',
195
+ type: 'obs',
196
+ questionOptions: {
197
+ rendering: 'text',
198
+ concept: '1c43b05b-b6d8-4eb5-8f37-0b14f5347568',
199
+ },
200
+ meta: {
201
+ previousValue: {
202
+ uuid: '305ed1fc-c1fd-11eb-8529-0242ac130003',
203
+ person: '833db896-c1f0-11eb-8529-0242ac130003',
204
+ concept: '1c43b05b-b6d8-4eb5-8f37-0b14f5347568',
205
+ location: { uuid: '41e6e516-c1f0-11eb-8529-0242ac130003' },
206
+ order: null,
207
+ groupMembers: [],
208
+ voided: false,
209
+ value: 'Can be discharged in next visit',
210
+ },
211
+ },
212
+ id: 'visit-note',
213
+ };
214
+
215
+ // replay
216
+ ObsAdapter.transformFieldValue(field, 'Discharged with minor symptoms', formContext);
217
+
218
+ // verify
219
+ expect(field.meta.submission.newValue).toEqual({
220
+ uuid: '305ed1fc-c1fd-11eb-8529-0242ac130003',
221
+ formFieldNamespace: 'rfe-forms',
222
+ formFieldPath: 'rfe-forms-visit-note',
223
+ value: 'Discharged with minor symptoms',
224
+ });
225
+ expect(field.meta.submission.voidedValue).toBe(null);
226
+ });
227
+
228
+ it('should edit obs coded value in edit mode', () => {
229
+ // setup
230
+ formContext.sessionMode = 'edit';
231
+ const field: FormField = {
232
+ label: 'HTS Result',
233
+ type: 'obs',
234
+ questionOptions: {
235
+ rendering: 'radio',
236
+ concept: '1c43b05b-b6d8-4eb5-8f37-0b14f5347568',
237
+ },
238
+ meta: {
239
+ previousValue: {
240
+ uuid: '305ed1fc-c1fd-11eb-8529-0242ac130003',
241
+ person: '833db896-c1f0-11eb-8529-0242ac130003',
242
+ concept: '1c43b05b-b6d8-4eb5-8f37-0b14f5347568',
243
+ location: { uuid: '41e6e516-c1f0-11eb-8529-0242ac130003' },
244
+ order: null,
245
+ groupMembers: [],
246
+ voided: false,
247
+ value: '5197ca4f-f0f7-4e63-9a68-8614224dce44',
248
+ },
249
+ },
250
+ id: 'hts-result',
251
+ };
252
+ // replay
253
+ ObsAdapter.transformFieldValue(field, 'a7fd300b-f4b5-4cd1-94f8-915adf61a5e3', formContext);
254
+ // verify
255
+ expect(field.meta.submission.newValue).toEqual({
256
+ uuid: '305ed1fc-c1fd-11eb-8529-0242ac130003',
257
+ formFieldNamespace: 'rfe-forms',
258
+ formFieldPath: 'rfe-forms-hts-result',
259
+ value: 'a7fd300b-f4b5-4cd1-94f8-915adf61a5e3',
260
+ });
261
+ expect(field.meta.submission.voidedValue).toBe(null);
262
+ });
263
+
264
+ it('should edit obs value(s) from multiselect input component', () => {
265
+ // setup
266
+ formContext.sessionMode = 'edit';
267
+ const field: FormField = {
268
+ label: 'Past enrolled patient programs',
269
+ type: 'obs',
270
+ questionOptions: {
271
+ rendering: 'checkbox',
272
+ concept: '3hbkj9-b6d8-4eju-8f37-0b14f5347jv9',
273
+ answers: [
274
+ { label: 'Option 1', concept: '105e7ad6-c1fd-11eb-8529-0242ac130ju9' },
275
+ { label: 'Option 2', concept: '305e77c0-c1fd-11eb-8529-0242ac130003' },
276
+ ],
277
+ },
278
+ meta: {
279
+ previousValue: [
280
+ {
281
+ uuid: 'f2487de5-e55f-4689-8791-0c919179818b',
282
+ person: '833db896-c1f0-11eb-8529-0242ac130003',
283
+ concept: '3hbkj9-b6d8-4eju-8f37-0b14f5347jv9',
284
+ location: { uuid: '41e6e516-c1f0-11eb-8529-0242ac130003' },
285
+ order: null,
286
+ groupMembers: [],
287
+ voided: false,
288
+ formFieldNamespace: 'rfe-forms',
289
+ formFieldPath: 'rfe-forms-past-patient-programs',
290
+ value: {
291
+ uuid: '105e7ad6-c1fd-11eb-8529-0242ac130ju9',
292
+ },
293
+ },
294
+ ],
295
+ },
296
+ id: 'past-patient-programs',
297
+ };
298
+
299
+ // replay
300
+ ObsAdapter.transformFieldValue(
301
+ field,
302
+ ['105e7ad6-c1fd-11eb-8529-0242ac130ju9', '305e77c0-c1fd-11eb-8529-0242ac130003'],
303
+ formContext,
304
+ );
305
+
306
+ // verify
307
+ expect(field.meta.submission.newValue).toEqual([
308
+ {
309
+ concept: '3hbkj9-b6d8-4eju-8f37-0b14f5347jv9',
310
+ formFieldNamespace: 'rfe-forms',
311
+ formFieldPath: 'rfe-forms-past-patient-programs',
312
+ value: '305e77c0-c1fd-11eb-8529-0242ac130003',
313
+ },
314
+ ]);
315
+ expect(field.meta.submission.voidedValue).toBe(null);
316
+ });
317
+
318
+ it('should edit obs date value in edit mode', () => {
319
+ // setup
320
+ formContext.sessionMode = 'edit';
321
+ const field: FormField = {
322
+ label: 'HTS date',
323
+ type: 'obs',
324
+ datePickerFormat: 'calendar',
325
+ questionOptions: {
326
+ rendering: 'date',
327
+ concept: '3e432ad5-7b19-4866-a68f-abf0d9f52a01',
328
+ },
329
+ meta: {
330
+ previousValue: {
331
+ uuid: 'bca7277f-a726-4d3d-9db8-40937228ead5',
332
+ person: '833db896-c1f0-11eb-8529-0242ac130003',
333
+ concept: '3e432ad5-7b19-4866-a68f-abf0d9f52a01',
334
+ location: { uuid: '41e6e516-c1f0-11eb-8529-0242ac130003' },
335
+ order: null,
336
+ groupMembers: [],
337
+ voided: false,
338
+ value: new Date(2020, 11, 16),
339
+ },
340
+ },
341
+ id: 'hts-date',
342
+ };
343
+ const newHtsDate = new Date(2021, 11, 16);
344
+ // replay
345
+ ObsAdapter.transformFieldValue(field, newHtsDate, formContext);
346
+ // verify
347
+ expect(field.meta.submission.newValue).toEqual({
348
+ uuid: 'bca7277f-a726-4d3d-9db8-40937228ead5',
349
+ formFieldNamespace: 'rfe-forms',
350
+ formFieldPath: 'rfe-forms-hts-date',
351
+ value: '2021-12-16',
352
+ });
353
+ expect(field.meta.submission.voidedValue).toBe(null);
354
+ });
355
+
356
+ // deleting/voiding existing values (edit mode)
357
+ it('should void deleted obs text/number value in edit mode', () => {
358
+ // setup
359
+ formContext.sessionMode = 'edit';
360
+ const field: FormField = {
361
+ label: 'Visit note',
362
+ type: 'obs',
363
+ questionOptions: {
364
+ rendering: 'text',
365
+ concept: '1c43b05b-b6d8-4eb5-8f37-0b14f5347568',
366
+ },
367
+ meta: {
368
+ previousValue: {
369
+ uuid: '305ed1fc-c1fd-11eb-8529-0242ac130003',
370
+ person: '833db896-c1f0-11eb-8529-0242ac130003',
371
+ concept: '1c43b05b-b6d8-4eb5-8f37-0b14f5347568',
372
+ location: { uuid: '41e6e516-c1f0-11eb-8529-0242ac130003' },
373
+ order: null,
374
+ groupMembers: [],
375
+ voided: false,
376
+ value: 'Can be discharged in next visit',
377
+ },
378
+ },
379
+ id: 'visit-note',
380
+ };
381
+
382
+ // replay
383
+ ObsAdapter.transformFieldValue(field, '', formContext);
384
+
385
+ // verify
386
+ expect(field.meta.submission.voidedValue).toEqual({
387
+ uuid: '305ed1fc-c1fd-11eb-8529-0242ac130003',
388
+ voided: true,
389
+ });
390
+ expect(field.meta.submission.newValue).toBe(null);
391
+ });
392
+
393
+ it('should void deleted obs coded value in edit mode', () => {
394
+ // setup
395
+ formContext.sessionMode = 'edit';
396
+ const field: FormField = {
397
+ label: 'HTS Result',
398
+ type: 'obs',
399
+ questionOptions: {
400
+ rendering: 'content-switcher',
401
+ concept: '1c43b05b-b6d8-4eb5-8f37-0b14f5347568',
402
+ },
403
+ meta: {
404
+ previousValue: {
405
+ uuid: '305ed1fc-c1fd-11eb-8529-0242ac130003',
406
+ person: '833db896-c1f0-11eb-8529-0242ac130003',
407
+ concept: '1c43b05b-b6d8-4eb5-8f37-0b14f5347568',
408
+ location: { uuid: '41e6e516-c1f0-11eb-8529-0242ac130003' },
409
+ order: null,
410
+ groupMembers: [],
411
+ voided: false,
412
+ value: '5197ca4f-f0f7-4e63-9a68-8614224dce44',
413
+ },
414
+ },
415
+ id: 'hts-result',
416
+ };
417
+ // replay
418
+ ObsAdapter.transformFieldValue(field, null, formContext);
419
+ // verify
420
+ expect(field.meta.submission.voidedValue).toEqual({
421
+ uuid: '305ed1fc-c1fd-11eb-8529-0242ac130003',
422
+ voided: true,
423
+ });
424
+ });
425
+
426
+ it('should void deleted obs coded value(s) from a multiselect input component', () => {
427
+ // setup
428
+ formContext.sessionMode = 'edit';
429
+ const field: FormField = {
430
+ label: 'Past enrolled patient programs',
431
+ type: 'obs',
432
+ questionOptions: {
433
+ rendering: 'checkbox',
434
+ concept: '3hbkj9-b6d8-4eju-8f37-0b14f5347jv9',
435
+ answers: [{ label: 'Option 1', concept: '105e7ad6-c1fd-11eb-8529-0242ac130ju9' }],
436
+ },
437
+ meta: {
438
+ previousValue: [
439
+ {
440
+ uuid: 'f2487de5-e55f-4689-8791-0c919179818b',
441
+ person: '833db896-c1f0-11eb-8529-0242ac130003',
442
+ concept: '3hbkj9-b6d8-4eju-8f37-0b14f5347jv9',
443
+ location: { uuid: '41e6e516-c1f0-11eb-8529-0242ac130003' },
444
+ order: null,
445
+ groupMembers: [],
446
+ voided: false,
447
+ value: {
448
+ uuid: '105e7ad6-c1fd-11eb-8529-0242ac130ju9',
449
+ },
450
+ },
451
+ ],
452
+ },
453
+ id: 'past-patient-programs',
454
+ };
455
+
456
+ // replay
457
+ ObsAdapter.transformFieldValue(field, [], formContext);
458
+
459
+ // verify
460
+ expect(field.meta.submission.voidedValue).toEqual([
461
+ {
462
+ uuid: 'f2487de5-e55f-4689-8791-0c919179818b',
463
+ voided: true,
464
+ },
465
+ ]);
466
+ expect(field.meta.submission.newValue).toBe(null);
467
+ });
468
+
469
+ it('should void deleted obs date value in edit mode', () => {
470
+ // setup
471
+ formContext.sessionMode = 'edit';
472
+ const htsDate = new Date(2020, 11, 16);
473
+ const field: FormField = {
474
+ label: 'HTS date',
475
+ type: 'obs',
476
+ datePickerFormat: 'calendar',
477
+ questionOptions: {
478
+ rendering: 'date',
479
+ concept: '3e432ad5-7b19-4866-a68f-abf0d9f52a01',
480
+ },
481
+ meta: {
482
+ previousValue: {
483
+ uuid: 'bca7277f-a726-4d3d-9db8-40937228ead5',
484
+ person: '833db896-c1f0-11eb-8529-0242ac130003',
485
+ concept: '3e432ad5-7b19-4866-a68f-abf0d9f52a01',
486
+ location: { uuid: '41e6e516-c1f0-11eb-8529-0242ac130003' },
487
+ order: null,
488
+ groupMembers: [],
489
+ voided: false,
490
+ value: htsDate,
491
+ },
492
+ },
493
+ id: 'hts-date',
494
+ };
495
+ // replay
496
+ ObsAdapter.transformFieldValue(field, '', formContext);
497
+ // verify
498
+ expect(field.meta.submission.voidedValue).toEqual({
499
+ uuid: 'bca7277f-a726-4d3d-9db8-40937228ead5',
500
+ voided: true,
501
+ });
502
+ expect(field.meta.submission.newValue).toBe(null);
503
+ });
504
+ });
505
+
506
+ describe('ObsAdapter - getInitialValue', () => {
507
+ it('should get initial value for text rendering', async () => {
508
+ // setup
509
+ const field: FormField = {
510
+ label: 'Visit note',
511
+ type: 'obs',
512
+ questionOptions: {
513
+ rendering: 'text',
514
+ concept: '1c43b05b-b6d8-4eb5-8f37-0b14f5347568',
515
+ },
516
+ id: 'visit-note',
517
+ };
518
+ const obs: any = {
519
+ uuid: '86a9366f-009b-40b7-b8ac-81fc6c4d7ca6',
520
+ concept: {
521
+ uuid: '1c43b05b-b6d8-4eb5-8f37-0b14f5347568',
522
+ },
523
+ value: 'Can be discharged in next visit',
524
+ };
525
+ formContext.domainObjectValue['obs'].push(obs);
526
+ // replay
527
+ const initialValue = await ObsAdapter.getInitialValue(field, formContext.domainObjectValue, formContext);
528
+ // verify
529
+ expect(initialValue).toBe('Can be discharged in next visit');
530
+ });
531
+
532
+ it('should get initial value for number rendering', async () => {
533
+ // setup
534
+ const field: FormField = {
535
+ label: 'Temperature',
536
+ type: 'obs',
537
+ questionOptions: {
538
+ rendering: 'number',
539
+ concept: '7928c3ab-4d14-471f-94a8-a12eaa59e29c',
540
+ },
541
+ id: 'temp',
542
+ };
543
+ const obs: any = {
544
+ uuid: '6ae85e6f-134d-48c2-b89a-8293d6ea7e3d',
545
+ concept: {
546
+ uuid: '7928c3ab-4d14-471f-94a8-a12eaa59e29c',
547
+ },
548
+ value: 37,
549
+ };
550
+ formContext.domainObjectValue['obs'].push(obs);
551
+ // replay
552
+ const initialValue = await ObsAdapter.getInitialValue(field, formContext.domainObjectValue, formContext);
553
+ // verify
554
+ expect(initialValue).toBe(37);
555
+ });
556
+
557
+ it('should get initial value for multi-checkbox rendering', async () => {
558
+ // setup
559
+ const field: FormField = {
560
+ label: 'Past enrolled patient programs',
561
+ type: 'obs',
562
+ questionOptions: {
563
+ rendering: 'checkbox',
564
+ concept: '3hbkj9-b6d8-4eju-8f37-0b14f5347jv9',
565
+ },
566
+ id: 'past-patient-programs',
567
+ };
568
+ const obsList: Array<any> = [
569
+ {
570
+ uuid: 'f2487de5-e55f-4689-8791-0c919179818b',
571
+ concept: {
572
+ uuid: '3hbkj9-b6d8-4eju-8f37-0b14f5347jv9',
573
+ },
574
+ value: {
575
+ uuid: '105e7ad6-c1fd-11eb-8529-0242ac130ju9',
576
+ },
577
+ },
578
+ {
579
+ uuid: '23fd1819-0eb2-4753-88d7-6fc015786c8d',
580
+ concept: {
581
+ uuid: '3hbkj9-b6d8-4eju-8f37-0b14f5347jv9',
582
+ },
583
+ value: {
584
+ uuid: '6f337e18-5445-437f-8298-684a7067dc1c',
585
+ },
586
+ },
587
+ ];
588
+ formContext.domainObjectValue['obs'] = obsList;
589
+ // replay
590
+ const initialValue = await ObsAdapter.getInitialValue(field, formContext.domainObjectValue, formContext);
591
+ // verify
592
+ expect(initialValue).toEqual(['105e7ad6-c1fd-11eb-8529-0242ac130ju9', '6f337e18-5445-437f-8298-684a7067dc1c']);
593
+ });
594
+
595
+ it('should get initial value for date rendering', async () => {
596
+ // setup
597
+ const field: FormField = {
598
+ label: 'HTS Date',
599
+ type: 'obs',
600
+ datePickerFormat: 'calendar',
601
+ questionOptions: {
602
+ rendering: 'date',
603
+ concept: 'j8b6705b-b6d8-4eju-8f37-0b14f5347569',
604
+ },
605
+ id: 'hts-date',
606
+ };
607
+ const obs: any = {
608
+ uuid: '828cff78-2c38-4ed2-94f1-61c5f79dda17',
609
+ concept: {
610
+ uuid: 'j8b6705b-b6d8-4eju-8f37-0b14f5347569',
611
+ },
612
+ value: '2016-11-19T00:00',
613
+ };
614
+ formContext.domainObjectValue['obs'].push(obs);
615
+ // replay
616
+ const initialValue: any = await ObsAdapter.getInitialValue(field, formContext.domainObjectValue, formContext);
617
+ // verify
618
+ expect(initialValue.toLocaleDateString('en-US')).toEqual('11/19/2016');
619
+ });
620
+
621
+ it('should get initial value for coded input types', async () => {
622
+ // setup
623
+ const field: FormField = {
624
+ label: 'HTS Result',
625
+ type: 'obs',
626
+ questionOptions: {
627
+ rendering: 'radio',
628
+ concept: '4e59df68-9774-49b3-9d33-ab75139c6a68',
629
+ },
630
+ id: 'hts-result',
631
+ };
632
+ const obs: any = {
633
+ uuid: '305ed1fc-c1fd-11eb-8529-0242ac130003',
634
+ concept: {
635
+ uuid: '4e59df68-9774-49b3-9d33-ab75139c6a68',
636
+ },
637
+ value: {
638
+ uuid: '12f7be3d-fb5d-47dc-b5e3-56c501be80a6',
639
+ },
640
+ };
641
+ formContext.domainObjectValue['obs'].push(obs);
642
+ // replay
643
+ const initialValue = await ObsAdapter.getInitialValue(field, formContext.domainObjectValue, formContext);
644
+ // verify
645
+ expect(initialValue).toEqual('12f7be3d-fb5d-47dc-b5e3-56c501be80a6');
646
+ });
647
+
648
+ it('should get initial values for obs-group members', async () => {
649
+ // setup
650
+ const basePath = 'rfe-forms-';
651
+ const groupingQuestion: FormField = {
652
+ label: 'Obs Group',
653
+ type: 'obsGroup',
654
+ questionOptions: {
655
+ rendering: 'group',
656
+ concept: '3e59df68-v77c-49b3-9d33-aS75133c6a67',
657
+ },
658
+ questions: [
659
+ {
660
+ label: 'Past enrolled patient programs',
661
+ type: 'obs',
662
+ questionOptions: {
663
+ rendering: 'checkbox',
664
+ concept: '3hbkj9-b6d8-4eju-8f37-0b14f5347jv9',
665
+ },
666
+ id: 'past-patient-programs',
667
+ },
668
+ {
669
+ label: 'Visit note',
670
+ type: 'obs',
671
+ questionOptions: {
672
+ rendering: 'text',
673
+ concept: '1c43b05b-b6d8-4eb5-8f37-0b14f5347568',
674
+ },
675
+ id: 'visit-note',
676
+ },
677
+ ],
678
+ id: 'obs-group',
679
+ };
680
+ const obsList: Array<any> = [
681
+ {
682
+ uuid: 'm2487deh-e55f-4689-8791-nc9191798180',
683
+ concept: {
684
+ uuid: '3e59df68-v77c-49b3-9d33-aS75133c6a67',
685
+ },
686
+ groupMembers: [
687
+ {
688
+ uuid: 'f2487de5-e55f-4689-8791-0c919179818b',
689
+ concept: {
690
+ uuid: '3hbkj9-b6d8-4eju-8f37-0b14f5347jv9',
691
+ },
692
+ value: {
693
+ uuid: '105e7ad6-c1fd-11eb-8529-0242ac130ju9',
694
+ },
695
+ obsGroup: {
696
+ uuid: 'm2487deh-e55f-4689-8791-nc9191798180',
697
+ },
698
+ formFieldPath: basePath + 'past-patient-programs',
699
+ },
700
+ {
701
+ uuid: '23fd1819-0eb2-4753-88d7-6fc015786c8d',
702
+ concept: {
703
+ uuid: '3hbkj9-b6d8-4eju-8f37-0b14f5347jv9',
704
+ },
705
+ value: {
706
+ uuid: '6f337e18-5445-437f-8298-684a7067dc1c',
707
+ },
708
+ obsGroup: {
709
+ uuid: 'm2487deh-e55f-4689-8791-nc9191798180',
710
+ },
711
+ formFieldPath: basePath + 'past-patient-programs',
712
+ },
713
+ {
714
+ uuid: '86a9366f-009b-40b7-b8ac-81fc6c4d7ca6',
715
+ concept: {
716
+ uuid: '1c43b05b-b6d8-4eb5-8f37-0b14f5347568',
717
+ },
718
+ value: 'Can be discharged in next visit',
719
+ obsGroup: {
720
+ uuid: 'm2487deh-e55f-4689-8791-nc9191798180',
721
+ },
722
+ formFieldPath: basePath + 'visit-note',
723
+ },
724
+ ],
725
+ },
726
+ ];
727
+
728
+ formContext.domainObjectValue['obs'] = obsList;
729
+
730
+ // past enrolled programs init value
731
+ let initialValue = await ObsAdapter.getInitialValue(
732
+ groupingQuestion.questions[0],
733
+ formContext.domainObjectValue,
734
+ formContext,
735
+ );
736
+ expect(initialValue).toEqual(['105e7ad6-c1fd-11eb-8529-0242ac130ju9', '6f337e18-5445-437f-8298-684a7067dc1c']);
737
+
738
+ // visit note init value
739
+ initialValue = await ObsAdapter.getInitialValue(
740
+ groupingQuestion.questions[1],
741
+ formContext.domainObjectValue,
742
+ formContext,
743
+ );
744
+ expect(initialValue).toEqual('Can be discharged in next visit');
745
+ });
746
+ });
747
+
748
+ describe('hasPreviousObsValueChanged', () => {
749
+ it('should support coded values', () => {
750
+ const codedField = {
751
+ questionOptions: {
752
+ rendering: 'radio',
753
+ },
754
+ meta: {
755
+ previousValue: {
756
+ value: {
757
+ uuid: '1065AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
758
+ },
759
+ },
760
+ },
761
+ } as any as FormField;
762
+
763
+ expect(hasPreviousObsValueChanged(codedField, '1065AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA')).toBe(false);
764
+ expect(hasPreviousObsValueChanged(codedField, '1066AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA')).toBe(true);
765
+ });
766
+ it('should support date values', () => {
767
+ const dateField = {
768
+ datePickerFormat: 'calendar',
769
+ questionOptions: {
770
+ rendering: 'date',
771
+ },
772
+ meta: {
773
+ previousValue: {
774
+ value: '2024-05-01T19:49:50.000+0000',
775
+ },
776
+ },
777
+ } as any as FormField;
778
+ expect(hasPreviousObsValueChanged(dateField, new Date('2024-05-01T19:49:50.000+0000'))).toBe(false);
779
+ expect(hasPreviousObsValueChanged(dateField, new Date('2024-05-02T15:49:50.000+0000'))).toBe(true);
780
+ });
781
+ it('should support datetime values', () => {
782
+ const dateTimeField = {
783
+ datePickerFormat: 'both',
784
+ questionOptions: {
785
+ rendering: 'datetime',
786
+ },
787
+ meta: {
788
+ previousValue: {
789
+ value: '2024-04-01T19:50:00.000+0000',
790
+ },
791
+ },
792
+ } as any as FormField;
793
+ expect(hasPreviousObsValueChanged(dateTimeField, new Date('2024-04-01T19:50:00.000+0000'))).toBe(false);
794
+ expect(hasPreviousObsValueChanged(dateTimeField, new Date('2024-04-01T19:40:40.000+0000'))).toBe(true);
795
+ });
796
+ it('should support free text', () => {
797
+ const textField = {
798
+ questionOptions: {
799
+ rendering: 'text',
800
+ },
801
+ meta: {
802
+ previousValue: {
803
+ value: 'Text value',
804
+ },
805
+ },
806
+ } as any as FormField;
807
+ expect(hasPreviousObsValueChanged(textField, 'Text value')).toBe(false);
808
+ expect(hasPreviousObsValueChanged(textField, 'Edited')).toBe(true);
809
+ });
810
+ });
811
+
812
+ describe('findObsByFormField', () => {
813
+ const namespace = 'rfe-forms';
814
+ const fields: Array<FormField> = [
815
+ {
816
+ label: 'Field One',
817
+ type: 'obs',
818
+ questionOptions: {
819
+ rendering: 'select',
820
+ concept: '8c3db896-c1f0-11eb-8529-0242acv30003',
821
+ },
822
+ id: 'fieldOne',
823
+ },
824
+ {
825
+ label: 'Field two',
826
+ type: 'obs',
827
+ questionOptions: {
828
+ rendering: 'select',
829
+ concept: '8c3db896-c1f0-11eb-8529-0242acv30003',
830
+ },
831
+ id: 'fieldTwo',
832
+ },
833
+ {
834
+ label: 'Field three',
835
+ type: 'obs',
836
+ questionOptions: {
837
+ rendering: 'select',
838
+ concept: 'mc3db896-c4f0-11eb-8529-0242acv3000c',
839
+ },
840
+ id: 'fieldThree',
841
+ },
842
+ {
843
+ label: 'Field four',
844
+ type: 'obs',
845
+ questionOptions: {
846
+ rendering: 'select',
847
+ concept: 'mc3db896-c4f0-11eb-8529-0242acv3000c',
848
+ },
849
+ id: 'fieldFour',
850
+ },
851
+ ];
852
+
853
+ const obsList: Array<any> = [
854
+ {
855
+ uuid: '6449d61a-7841-4aaf-a956-e6b1bd731385',
856
+ concept: {
857
+ uuid: '8c3db896-c1f0-11eb-8529-0242acv30003',
858
+ },
859
+ formFieldNamespace: namespace,
860
+ formFieldPath: 'rfe-forms-fieldOne',
861
+ },
862
+ {
863
+ uuid: '1449d61a-78b1-4aaf-a956-e6b1bd73138f',
864
+ concept: {
865
+ uuid: 'mc3db896-c4f0-11eb-8529-0242acv3000c',
866
+ },
867
+ formFieldNamespace: namespace,
868
+ formFieldPath: 'rfe-forms-fieldThree',
869
+ },
870
+ {
871
+ uuid: '8449d61a-5841-4aaf-a956-e6b1bd73138b',
872
+ concept: {
873
+ uuid: '8c3db896-c1f0-11eb-8529-0242acv30003',
874
+ },
875
+ formFieldNamespace: namespace,
876
+ formFieldPath: 'rfe-forms-fieldTwo',
877
+ },
878
+ {
879
+ uuid: '5449d61a-4841-4aaf-a956-26b1bd73138b',
880
+ concept: {
881
+ uuid: 'mc3db896-c4f0-11eb-8529-0242acv3000c',
882
+ },
883
+ formFieldNamespace: 'some-random-namespace',
884
+ formFieldPath: 'none-existing-pathname',
885
+ },
886
+ ];
887
+
888
+ it('Should find observation by field path', () => {
889
+ // do find
890
+ let matchedObs = findObsByFormField(obsList, [], fields[0]);
891
+ // verify
892
+ expect(matchedObs.length).toBe(1);
893
+ expect(matchedObs[0]).toBe(obsList[0]);
894
+ // replay
895
+ matchedObs = findObsByFormField(obsList, [], fields[1]);
896
+ // verify
897
+ expect(matchedObs.length).toBe(1);
898
+ expect(matchedObs[0]).toBe(obsList[2]);
899
+ });
900
+
901
+ it('Should fallback to mapping by concept if no obs was found by fieldpath', () => {
902
+ // do find
903
+ const matchedObs = findObsByFormField(obsList, [obsList[1].uuid], fields[3]);
904
+ // verify
905
+ expect(matchedObs.length).toBe(1);
906
+ expect(matchedObs[0]).toBe(obsList[3]);
907
+ });
908
+ });
@@ -162,15 +162,17 @@ describe('Text field input', () => {
162
162
  await renderForm(textValues);
163
163
  const inputField = screen.getByLabelText('Indicate your notes');
164
164
 
165
- await user.type(inputField, 'Updated patient notese');
166
-
167
- expect(mockSetFieldValue).toHaveBeenCalledWith(
168
- expect.objectContaining({
169
- target: expect.objectContaining({
170
- value: 'Updated patient notese',
165
+ await user.type(inputField, 'Updated patient notes');
166
+
167
+ await act(async () => {
168
+ expect(mockSetFieldValue).toHaveBeenCalledWith(
169
+ expect.objectContaining({
170
+ target: expect.objectContaining({
171
+ value: 'Updated patient notes',
172
+ }),
171
173
  }),
172
- }),
173
- );
174
+ );
175
+ });
174
176
  });
175
177
 
176
178
  it('should have value passed in as prop', async () => {
@@ -127,9 +127,18 @@ export const FormFactoryProvider: React.FC<FormFactoryProviderProps> = ({
127
127
  handleClose();
128
128
  }
129
129
  })
130
- .catch((toastErrorObject: ToastDescriptor) => {
130
+ .catch((errorObject: Error | ToastDescriptor) => {
131
131
  setIsSubmitting(false);
132
- showToast(toastErrorObject);
132
+ if (errorObject instanceof Error) {
133
+ showToast({
134
+ title: t('errorProcessingFormSubmission', 'Error processing form submission'),
135
+ kind: 'error',
136
+ description: errorObject.message,
137
+ critical: true,
138
+ });
139
+ } else {
140
+ showToast(errorObject);
141
+ }
133
142
  });
134
143
  } else {
135
144
  setIsSubmitting(false);