diff --git a/__mocks__/forms/rfe-forms/diagnosis-test-form.json b/__mocks__/forms/rfe-forms/diagnosis-test-form.json new file mode 100644 index 00000000..c60ffdb9 --- /dev/null +++ b/__mocks__/forms/rfe-forms/diagnosis-test-form.json @@ -0,0 +1,60 @@ +{ + "name": "Diagnosis test", + "pages": [ + { + "label": "1", + "sections": [ + { + "label": "Select diagnosis", + "isExpanded": "true", + "questions": [ + { + "label": "Test Diagnosis 1", + "id": "diagnosis1", + "type": "diagnosis", + "questionOptions": { + "rendering": "repeating", + "dataSource": "diagnoses", + "isSearchable": "false", + "diagnosis": { + "isConfirmed": "true", + "rank": 1, + "conceptClasses": [ + "8d4918b0-c2cc-11de-8d13-0010c6dffd0f", + "8d492954-c2cc-11de-8d13-0010c6dffd0f", + "8d492b2a-c2cc-11de-8d13-0010c6dffd0f" + ] + } + } + }, + { + "label": "Test Diagnosis 2", + "id": "diagnosis2", + "type": "diagnosis", + "questionOptions": { + "rendering": "repeating", + "dataSource": "diagnoses", + "isSearchable": "false", + "diagnosis": { + "isConfirmed": "true", + "rank": 1, + "conceptClasses": [ + "8d4918b0-c2cc-11de-8d13-0010c6dffd0f", + "8d492954-c2cc-11de-8d13-0010c6dffd0f", + "8d492b2a-c2cc-11de-8d13-0010c6dffd0f" + ] + } + } + } + ] + } + ] + } + ], + "processor": "EncounterFormProcessor", + "encounterType": "e22e39fd-7db2-45e7-80f1-60fa0d5a4378", + "referencedForms": [], + "uuid": "29ce67d1-892b-45be-84ce-8e36aa9ca37f", + "description": "re", + "version": "1.0" +} diff --git a/src/adapters/encounter-diagnosis-adapter.ts b/src/adapters/encounter-diagnosis-adapter.ts index af3af17e..b218bb0b 100644 --- a/src/adapters/encounter-diagnosis-adapter.ts +++ b/src/adapters/encounter-diagnosis-adapter.ts @@ -4,7 +4,6 @@ import { type FormContextProps } from '../provider/form-provider'; import { type OpenmrsEncounter, type FormField } from '../types'; import { gracefullySetSubmission } from '../utils/common-utils'; import { isEmpty } from '../validators/form-validator'; -import { voidObs } from './obs-adapter'; import { isTrue } from '../utils/boolean-utils'; export let assignedDiagnosesIds: string[] = []; @@ -12,7 +11,7 @@ export let assignedDiagnosesIds: string[] = []; export const EncounterDiagnosisAdapter: FormFieldValueAdapter = { transformFieldValue: function (field: FormField, value: any, context: FormContextProps) { if (field.meta.initialValue?.omrsObject && isEmpty(value)) { - return gracefullySetSubmission(field, undefined, voidObs(field.meta.initialValue.omrsObject as OpenmrsObs)); + return gracefullySetSubmission(field, undefined, voidDiagnosis(field.meta.initialValue.omrsObject as OpenmrsObs)); } if (!isEmpty(value)) { const previousDiagnosis = field.meta.initialValue?.omrsObject as OpenmrsResource; @@ -68,7 +67,7 @@ export const EncounterDiagnosisAdapter: FormFieldValueAdapter = { }, }; -const constructNewDiagnosis = (value: any, field: FormField, patientUuid: string) => { +const constructNewDiagnosis = (value: string, field: FormField, patientUuid: string) => { if (!value) { return null; } @@ -86,7 +85,7 @@ const constructNewDiagnosis = (value: any, field: FormField, patientUuid: string }; function editDiagnosis( - newEncounterDiagnosis: any, + newEncounterDiagnosis: string, field: FormField, previousDiagnosis: OpenmrsResource, patientUuid: string, @@ -105,9 +104,13 @@ function editDiagnosis( }; } -export function hasPreviousDiagnosisValueChanged(previousDiagnosis: OpenmrsResource, newValue: any) { +export function hasPreviousDiagnosisValueChanged(previousDiagnosis: OpenmrsResource, newValue: string) { if (isEmpty(previousDiagnosis)) { return false; } return previousDiagnosis.value !== newValue; } + +export function voidDiagnosis(obs: OpenmrsObs) { + return { uuid: obs.uuid, voided: true }; +} diff --git a/src/form-engine.test.tsx b/src/form-engine.test.tsx index 2af05c5a..a5e2b44c 100644 --- a/src/form-engine.test.tsx +++ b/src/form-engine.test.tsx @@ -1,7 +1,7 @@ import React from 'react'; import dayjs from 'dayjs'; import userEvent from '@testing-library/user-event'; -import { act, cleanup, render, screen, within } from '@testing-library/react'; +import { act, cleanup, render, screen, waitFor, within } from '@testing-library/react'; import { ExtensionSlot, OpenmrsDatePicker, @@ -44,14 +44,18 @@ import viralLoadStatusForm from '__mocks__/forms/rfe-forms/viral-load-status-for import readOnlyValidationForm from '__mocks__/forms/rfe-forms/read-only-validation-form.json'; import jsExpressionValidationForm from '__mocks__/forms/rfe-forms/js-expression-validation-form.json'; import hidePagesAndSectionsForm from '__mocks__/forms/rfe-forms/hide-pages-and-sections-form.json'; +import diagnosisForm from '__mocks__/forms/rfe-forms/diagnosis-test-form.json'; import FormEngine from './form-engine.component'; import { type SessionMode } from './types'; +import { use } from 'i18next'; const patientUUID = '8673ee4f-e2ab-4077-ba55-4980f408773e'; const visit = mockVisit; const formsResourcePath = when((url: string) => url.includes(`${restBaseUrl}/form/`)); const clobDataResourcePath = when((url: string) => url.includes(`${restBaseUrl}/clobdata/`)); +const conceptResourcePath = when((url: string) => url.includes(`${restBaseUrl}/concept/`)); + global.ResizeObserver = require('resize-observer-polyfill'); const mockOpenmrsFetch = jest.mocked(openmrsFetch); @@ -60,25 +64,56 @@ const mockUsePatient = jest.mocked(usePatient); const mockUseSession = jest.mocked(useSession); const mockOpenmrsDatePicker = jest.mocked(OpenmrsDatePicker); -mockOpenmrsDatePicker.mockImplementation(({ id, labelText, value, onChange, isInvalid, invalidText }) => { - return ( - <> - - { - onChange(dayjs(evt.target.value).toDate()); - }} - /> - {isInvalid && {invalidText}} - - ); -}); +// mockOpenmrsDatePicker.mockImplementation(({ id, labelText, value, onChange, isInvalid, invalidText }) => { +// return ( +// <> +// +// { +// onChange(dayjs(evt.target.value).toDate()); +// }} +// /> +// {isInvalid && {invalidText}} +// +// ); +// }); when(mockOpenmrsFetch).calledWith(formsResourcePath).mockReturnValue({ data: demoHtsOpenmrsForm }); when(mockOpenmrsFetch).calledWith(clobDataResourcePath).mockReturnValue({ data: demoHtsForm }); +jest.mock('./registry/registry', () => { + const originalModule = jest.requireActual('./registry/registry'); + return { + ...originalModule, + getRegisteredDataSource: jest.fn().mockResolvedValue({ + fetchData: jest.fn().mockImplementation((...args) => { + if (args[1].class?.length && !args[1].referencedValue.key) { + // concept DS + return Promise.resolve([ + { + uuid: 'stage-1-uuid', + display: 'stage 1', + }, + { + uuid: 'stage-2-uuid', + display: 'stage 2', + }, + ]); + } + }), + fetchSingleItem: jest.fn().mockImplementation((uuid: string) => { + return Promise.resolve({ + uuid, + display: 'stage 1', + }); + }), + toUuidAndDisplay: (data) => data, + }), + }; +}); + jest.mock('../src/api', () => { const originalModule = jest.requireActual('../src/api'); @@ -1047,6 +1082,77 @@ describe('Form engine component', () => { }); }); + describe('Diagnisis field', () => { + it('should test addition of a diagnosis', async () => { + await act(async () => { + renderForm(null, diagnosisForm); + }); + const addButtons = screen.getAllByRole('button', { name: 'Add' }); + expect(addButtons.length).toBeGreaterThan(0); + await user.click(addButtons[0]); + expect(screen.getByRole('button', { name: /Remove/i })).toBeInTheDocument(); + expect(screen.getAllByRole('combobox', { name: /^test diagnosis 1$/i }).length).toEqual(2); + }); + + it('should render diagnosis field', async () => { + await act(async () => { + renderForm(null, diagnosisForm); + }); + const initialDiagnosis = screen.getAllByRole('combobox', { name: /test diagnosis 1|test diagnosis 2/i }); + expect(initialDiagnosis.length).toBe(2); + }); + + it('should save diagnosis field on form submission', async () => { + await act(async () => { + renderForm(null, diagnosisForm); + }); + const saveEncounterMock = jest.spyOn(api, 'saveEncounter'); + + const combobox = await screen.findByRole('combobox', { name: /test diagnosis 1/i }); + expect(combobox).toBeInTheDocument(); + + await userEvent.click(combobox); + await waitFor(() => { + expect(screen.getByText('stage 1')).toBeInTheDocument(); + expect(screen.getByText('stage 2')).toBeInTheDocument(); + }); + await user.click(screen.getByText('stage 1')); + await user.click(screen.getByRole('button', { name: /save/i })); + expect(saveEncounterMock).toHaveBeenCalledTimes(1); + const [_, encounter] = saveEncounterMock.mock.calls[0]; + expect(encounter.diagnoses.length).toBe(1); + expect(encounter.diagnoses[0]).toEqual({ + patient: '8673ee4f-e2ab-4077-ba55-4980f408773e', + condition: null, + diagnosis: { + coded: 'stage-1-uuid', + }, + certainty: 'CONFIRMED', + rank: 1, + formFieldPath: `rfe-forms-diagnosis1`, + formFieldNamespace: 'rfe-forms', + }); + }); + + it('should test removing of a diagnosis field', async () => { + await act(async () => { + renderForm(null, diagnosisForm); + }); + + const addButtons = screen.getAllByRole('button', { name: 'Add' }); + expect(addButtons.length).toBeGreaterThan(0); + await user.click(addButtons[0]); + expect(screen.getByRole('button', { name: /Remove/i })).toBeInTheDocument(); + expect(screen.getAllByRole('combobox', { name: /^test diagnosis 1$/i }).length).toEqual(2); + + const removeButton = screen.getByRole('button', { name: /Remove/i }); + + await user.click(removeButton); + + expect(removeButton).not.toBeInTheDocument(); + }); + }); + function renderForm(formUUID, formJson, intent?: string, mode?: SessionMode) { render(