Skip to content
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should also test for things like adding a grouped question renders the input fields for another question and that deleting an obsgroup question does actually delete it

Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import QuestionModal from './question.modal';
import { FormFieldProvider } from './form-field-context';
import { showSnackbar } from '@openmrs/esm-framework';
import type { FormField, RenderType } from '@openmrs/esm-form-engine-lib';
import type { Schema } from '@types';

jest.mock('@openmrs/esm-framework', () => ({
showSnackbar: jest.fn(),
useDebounce: jest.fn((val) => val),
}));

jest.mock('@hooks/useConceptLookup', () => ({
useConceptLookup: jest.fn(() => ({ concepts: [], conceptLookupError: null, isLoadingConcepts: false })),
}));

jest.mock('@hooks/useConceptId', () => ({
useConceptId: jest.fn(() => ({ concept: null, isLoading: false, error: null })),
}));

jest.mock('react-i18next', () => ({
useTranslation: () => ({
t: (key, defaultValue) => defaultValue || key,
}),
}));
Comment on lines +22 to +26
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this required? We already have a mock for react-i18next in https://github.com/openmrs/openmrs-esm-form-builder/blob/main/__mocks__/react-i18next.js


const mockSchema: Schema = {
name: 'Test Form',
encounterType: 'test-encounter',
pages: [
{
label: 'Test Page',
sections: [
{
label: 'Test Section',
isExpanded: 'true',
questions: [],
},
],
},
],
processor: 'EncounterFormProcessor',
uuid: 'test-uuid',
referencedForms: [],
};

const obsGroupFormField: FormField = {
id: 'vitalSigns',
label: 'Vital Signs',
type: 'obsGroup',
questionOptions: {
rendering: 'group' as RenderType,
concept: 'test-concept-group',
},
questions: [],
};

describe('QuestionModal Component - obsGroup Tests', () => {
const mockCloseModal = jest.fn();
const mockOnSchemaChange = jest.fn();
const mockResetIndices = jest.fn();

beforeEach(() => {
jest.clearAllMocks();
});

it('should render the question modal with obsGroup type', () => {
const initialFormField: FormField = {
...obsGroupFormField,
id: 'vitalSigns',
label: 'Vital Signs',
type: 'obsGroup',
questionOptions: {
rendering: 'group' as RenderType,
concept: 'test-concept-group',
},
};

render(
<FormFieldProvider initialFormField={initialFormField}>
<QuestionModal
closeModal={mockCloseModal}
schema={mockSchema}
pageIndex={0}
sectionIndex={0}
questionIndex={0}
onSchemaChange={mockOnSchemaChange}
resetIndices={mockResetIndices}
formField={initialFormField}
/>
</FormFieldProvider>,
);
Comment on lines +77 to +90
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prefer to have the `renderComponent as a separate function like so -

function renderComponent() {
render(
<FormFieldProvider initialFormField={formField}>
<PatientIdentifierTypeQuestion />
</FormFieldProvider>,
);


expect(screen.getByText(/Question type/i)).toBeInTheDocument();
expect(screen.getByText(/Rendering type/i)).toBeInTheDocument();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should also make sure that the button Add grouped question is visible

});
});
154 changes: 154 additions & 0 deletions src/resources/form-validator-obsgroup.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
import { handleFormValidation } from './form-validator.resource';
import type { Schema } from '@types';
import type { RenderType } from '@openmrs/esm-form-engine-lib';

interface ObsGroupQuestion {
id: string;
label: string;
type: string;
questionOptions: {
rendering: RenderType;
concept: string;
};
questions: Array<{
id: string;
label: string;
type: string;
questionOptions: {
rendering: RenderType;
concept: string;
};
}>;
}

jest.mock('./form-validator.resource', () => ({
handleFormValidation: jest.fn().mockImplementation((schema) => {
const errors = [];
const warnings = [];

if (schema.pages?.[0]?.sections?.[0]?.questions?.[0]?.type === 'obsGroup') {
const obsGroupQuestion = schema.pages[0].sections[0].questions[0];

if (!obsGroupQuestion.questionOptions?.concept || obsGroupQuestion.questionOptions.concept === '') {
errors.push({
field: { id: obsGroupQuestion.id },
errorMessage: 'Concept is required for obsGroup questions',
});
}

if (obsGroupQuestion.questions) {
obsGroupQuestion.questions.forEach((childQuestion) => {
if (!childQuestion.questionOptions?.concept || childQuestion.questionOptions.concept === '') {
errors.push({
field: { id: childQuestion.id },
errorMessage: 'Concept is required for child questions',
});
}
});
}
}

return Promise.resolve([errors, warnings]);
}),
}));

const createTestSchema = (): Schema => ({
name: 'Test Form',
encounterType: 'test-encounter',
pages: [
{
label: 'Test Page',
sections: [
{
label: 'Test Section',
isExpanded: 'true',
questions: [
{
id: 'vitalSigns',
label: 'Vital Signs',
type: 'obsGroup',
questionOptions: {
rendering: 'group' as RenderType,
Copy link
Contributor

@Bharath-K-Shetty Bharath-K-Shetty Mar 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need explicit casting here?

concept: 'test-concept-group',
},
questions: [
{
id: 'temperature',
label: 'Temperature',
type: 'obs',
questionOptions: {
rendering: 'number' as RenderType,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
rendering: 'number' as RenderType,
rendering: 'number',

concept: 'test-concept-temp',
},
},
{
id: 'bloodPressure',
label: 'Blood Pressure',
type: 'obs',
questionOptions: {
rendering: 'text' as RenderType,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
rendering: 'text' as RenderType,
rendering: 'text' ,

concept: 'test-concept-bp',
},
},
],
} as ObsGroupQuestion,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this as ObsGroupQuestion required?

],
},
],
},
],
processor: 'EncounterFormProcessor',
uuid: 'test-uuid',
referencedForms: [],
});

const createInvalidObsGroupSchema = (): Schema => {
const schema = createTestSchema();
const obsGroupQuestion = schema.pages[0].sections[0].questions[0] as ObsGroupQuestion;
obsGroupQuestion.questionOptions.concept = '';
return schema;
};

const createSchemaWithInvalidChildQuestion = (): Schema => {
const schema = createTestSchema();
const obsGroupQuestion = schema.pages[0].sections[0].questions[0] as ObsGroupQuestion;
const childQuestion = obsGroupQuestion.questions[0];
childQuestion.questionOptions.concept = '';
return schema;
};

describe('Form Validator - obsGroup Tests', () => {
beforeEach(() => {
jest.clearAllMocks();
});

it('should validate a valid obsGroup question with valid child questions', async () => {
const schema = createTestSchema();
const mockConfigObject = {};

const [errors, warnings] = await handleFormValidation(schema, mockConfigObject);

expect(errors.length).toBe(0);
expect(warnings.length).toBe(0);
}, 10000);

it('should detect errors in an invalid obsGroup question', async () => {
const schema = createInvalidObsGroupSchema();
const mockConfigObject = {};

const [errors, warnings] = await handleFormValidation(schema, mockConfigObject);

expect(errors.length).toBeGreaterThan(0);
expect(errors.some((error) => error.field.id === 'vitalSigns')).toBe(true);
}, 10000);

it('should detect errors in child questions of an obsGroup', async () => {
const schema = createSchemaWithInvalidChildQuestion();
const mockConfigObject = {};

const [errors, warnings] = await handleFormValidation(schema, mockConfigObject);

expect(errors.length).toBeGreaterThan(0);
expect(errors.some((error) => error.field.id === 'temperature')).toBe(true);
}, 10000);
});