Skip to content

Commit

Permalink
(test) Add tests for the Results Viewer tree view wrapper (#2084)
Browse files Browse the repository at this point in the history
* Add tests for tree view wrapper component

* code review

* Tweaks

---------

Co-authored-by: CynthiaKamau <[email protected]>
Co-authored-by: Dennis Kigen <[email protected]>
  • Loading branch information
3 people authored Nov 11, 2024
1 parent 0bd6885 commit b5ec1f3
Show file tree
Hide file tree
Showing 6 changed files with 151 additions and 9 deletions.
6 changes: 3 additions & 3 deletions packages/esm-patient-tests-app/src/config-schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,15 +88,15 @@ export interface LabTestReason {

export interface OrderReason {
labTestUuid: string;
required: boolean;
orderReasons: Array<string>;
required: boolean;
}

export interface ConfigObject {
resultsViewerConcepts: Array<ObsTreeEntry>;
labTestsWithOrderReasons: Array<OrderReason>;
orders: {
labOrderTypeUuid: string;
labOrderableConcepts: Array<string>;
};
labTestsWithOrderReasons: Array<OrderReason>;
resultsViewerConcepts: Array<ObsTreeEntry>;
}
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ const IndividualResultsTable: React.FC<IndividualResultsTableProps> = ({ isLoadi

const tableRows = useMemo(
() =>
subRows?.entries.length &&
subRows.entries.map((row, i) => {
const { units = '', range = '' } = row;
const isString = isNaN(parseFloat(row.value));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export function useObservations() {
const results = useMemo(() => {
const observations: Array<FHIRObservationResource> = data
? []
.concat(...data?.map((resp) => resp.data?.entry?.map((e) => e.resource) ?? []))
.concat(...data?.map((resp) => resp?.data?.entry?.map((e) => e.resource) ?? []))
.sort((obs1, obs2) => Date.parse(obs2.effectiveDateTime) - Date.parse(obs1.effectiveDateTime))
: null;
return {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React from 'react';
import { useTranslation } from 'react-i18next';
import { EmptyState, ErrorState } from '@openmrs/esm-patient-common-lib';
import { useConfig } from '@openmrs/esm-framework';
import { type ConfigObject } from '../../config-schema';
import { type viewOpts } from '../../types';
import { useGetManyObstreeData } from '../grouped-timeline';
import { FilterProvider } from '../filter/filter-context';
Expand All @@ -18,7 +19,7 @@ interface TreeViewWrapperProps {

const TreeViewWrapper: React.FC<TreeViewWrapperProps> = (props) => {
const { t } = useTranslation();
const config = useConfig();
const config = useConfig<ConfigObject>();
const conceptUuids = config?.resultsViewerConcepts?.map((c) => c.conceptUuid) ?? [];
const { roots, isLoading, error } = useGetManyObstreeData(conceptUuids);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import { useConfig, useLayoutType, usePatient } from '@openmrs/esm-framework';
import { mockPatient } from 'tools';
import { mockGroupedResults, mockResults } from '__mocks__';
import { type ConfigObject } from '../../config-schema';
import { type FilterContextProps } from '../filter/filter-types';
import { useGetManyObstreeData } from '../grouped-timeline';
import TreeViewWrapper from './tree-view-wrapper.component';
import FilterContext from '../filter/filter-context';

const mockUsePatient = jest.mocked(usePatient);
const mockUseConfig = jest.mocked(useConfig<ConfigObject>);
const mockUseLayoutType = jest.mocked(useLayoutType);
const mockUseGetManyObstreeData = jest.mocked(useGetManyObstreeData);

jest.mock('../panel-timeline/helpers', () => ({
...jest.requireActual('../panel-timeline/helpers'),
parseTime: jest.fn(),
}));

jest.mock('../grouped-timeline', () => ({
...jest.requireActual('../grouped-timeline'),
useGetManyObstreeData: jest.fn(),
}));

const mockProps = {
patientUuid: 'test-patient-uuid',
basePath: '/test-base-path',
testUuid: 'test-uuid',
expanded: false,
type: 'default',
view: 'individual-test' as const,
};

const mockFilterContext: FilterContextProps = {
activeTests: ['Bloodwork-Chemistry', 'Bloodwork'],
timelineData: mockGroupedResults.timelineData,
parents: mockGroupedResults.parents,
checkboxes: { Bloodwork: false, Chemistry: true },
someChecked: true,
lowestParents: mockGroupedResults['lowestParents'],
totalResultsCount: 0,
initialize: jest.fn(),
toggleVal: jest.fn(),
updateParent: jest.fn(),
resetTree: jest.fn(),
roots: mockResults,
tests: {},
};

const renderTreeViewWrapperWithMockContext = (contextValue = mockFilterContext) => {
render(
<FilterContext.Provider value={contextValue}>
<TreeViewWrapper {...mockProps} />
</FilterContext.Provider>,
);
};

describe('TreeViewWrapper', () => {
beforeEach(() => {
mockUseLayoutType.mockReturnValue('small-desktop');

mockUsePatient.mockReturnValue({
patient: mockPatient,
patientUuid: mockPatient.id,
isLoading: false,
error: null,
});

mockUseConfig.mockReturnValue({
resultsViewerConcepts: [
{
conceptUuid: '9a6f10d6-7fc5-4fb7-9428-24ef7b8d01f7',
defaultOpen: true,
},
{
conceptUuid: '856AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
defaultOpen: true,
},
{
conceptUuid: '1015AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
defaultOpen: false,
},
],
orders: {
labOrderTypeUuid: '52a447d3-a64a-11e3-9aeb-50e549534c5e',
labOrderableConcepts: ['1748a953-d12e-4be1-914c-f6b096c6cdef'],
},
labTestsWithOrderReasons: [],
});
});

it('renders an empty state view when there is no data', () => {
mockUseGetManyObstreeData.mockReturnValue({
roots: [],
isLoading: false,
error: null,
});

render(<TreeViewWrapper {...mockProps} />);

expect(screen.getByRole('heading', { name: /test results/i })).toBeInTheDocument();
expect(screen.getByText(/there are no test results data to display for this patient/i)).toBeInTheDocument();
});

it('renders an error state when there is an error', () => {
const mockError = new Error('Test error');
mockUseGetManyObstreeData.mockReturnValue({
roots: [],
isLoading: false,
error: mockError,
});

render(<TreeViewWrapper {...mockProps} />);

expect(screen.getByRole('heading', { name: /data load error/i })).toBeInTheDocument();
expect(
screen.getByText(
/sorry, there was a problem displaying this information. you can try to reload this page, or contact the site administrator and quote the error code above./i,
),
).toBeInTheDocument();
});

it('renders the tree view when test data is successfully fetched', async () => {
mockUseGetManyObstreeData.mockReturnValue({
roots: mockResults,
isLoading: false,
error: null,
});

renderTreeViewWrapperWithMockContext();

expect(screen.getByRole('table')).toBeInTheDocument();
expect(screen.getAllByText('Complete blood count').length).toBeGreaterThan(0);
expect(screen.getAllByText('Haemoglobin').length).toBeGreaterThan(0);
expect(screen.getAllByText('Hematocrit').length).toBeGreaterThan(0);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import TabletOverlay from '../tablet-overlay';
import Trendline from '../trendline/trendline.component';
import usePanelData from '../panel-view/usePanelData';
import styles from '../results-viewer/results-viewer.scss';
import RecentOverview from '../overview/recent-overview.component';

interface TreeViewProps {
patientUuid: string;
Expand All @@ -32,7 +31,7 @@ const GroupedPanelsTables: React.FC<{ className: string; loadingPanelData: boole
const { checkboxes, someChecked, tableData } = useContext(FilterContext);
const selectedCheckboxes = Object.keys(checkboxes).filter((key) => checkboxes[key]);

if (tableData && tableData?.length === 0) {
if (!tableData?.length) {
return <EmptyState displayText={t('data', 'data')} headerTitle={t('dataTimelineText', 'Data timeline')} />;
}

Expand Down Expand Up @@ -62,17 +61,19 @@ const GroupedPanelsTables: React.FC<{ className: string; loadingPanelData: boole
};

const TreeView: React.FC<TreeViewProps> = ({ patientUuid, basePath, testUuid, isLoading, expanded, type, view }) => {
const { t } = useTranslation();
const tablet = useLayoutType() === 'tablet';
const [showTreeOverlay, setShowTreeOverlay] = useState(false);
const { t } = useTranslation();

const { timelineData, resetTree } = useContext(FilterContext);
const { isLoading: isLoadingPanelData } = usePanelData();

if (tablet) {
return (
<>
<div>{!isLoading ? <GroupedTimeline patientUuid={patientUuid} /> : <DataTableSkeleton />}</div>
<div>
{!isLoading ? <GroupedTimeline patientUuid={patientUuid} /> : <DataTableSkeleton role="progressbar" />}
</div>
<div className={styles.floatingTreeButton}>
<Button
renderIcon={TreeViewAltIcon}
Expand Down

0 comments on commit b5ec1f3

Please sign in to comment.