Skip to content

Commit

Permalink
(tests) O3-4107 Add tests for the clinical views dashboard page (#15)
Browse files Browse the repository at this point in the history
  • Loading branch information
CynthiaKamau authored Oct 28, 2024
1 parent 760102b commit 9ce5d8a
Show file tree
Hide file tree
Showing 5 changed files with 112 additions and 94 deletions.
68 changes: 3 additions & 65 deletions __mocks__/content-packages.mock.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
export const mockContentPackages = [
import { type ContentPackage } from '../src/types';

export const mockContentPackages: ContentPackage[] = [
{
'@openmrs/esm-patient-chart-app': {
extensionSlots: {
Expand All @@ -10,14 +12,6 @@ export const mockContentPackages = [
slotName: 'hiv-care-and-treatment-group-slot',
isExpanded: true,
},
'Translation overrides': {
en: {
hIVCareAndTreatment: 'HIV Care And Treatment',
},
fr: {
hIVCareAndTreatment: 'Soins et traitement du VIH',
},
},
},
},
'hiv-care-and-treatment-group-slot': {
Expand Down Expand Up @@ -134,32 +128,6 @@ export const mockContentPackages = [
},
],
},
'Translation overrides': {
en: {
clinicalVisit: 'Clinical Visit',
clinicalVisitEncounters: 'clinical visit encounters',
visitDate: 'Visit Date',
visitType: 'Visit Type',
tbScreeningOutcome: 'TB Screening Outcome',
nextAppointmentDate: 'Next Appointment Date',
actionsTitle: 'Actions',
viewDetails: 'View Details',
editForm: 'Edit Form',
add: 'Add',
},
fr: {
clinicalVisit: 'Visite clinique',
clinicalVisitEncounters: 'Rencontres de visite clinique',
visitDate: 'Date de visite',
visitType: 'Type de visite',
tbScreeningOutcome: 'Résultat du dépistage de la tuberculose',
nextAppointmentDate: 'Date du prochain rendez-vous',
actionsTitle: 'Actions',
viewDetails: 'Voir les détails',
editForm: 'Modifier le formulaire',
add: 'Ajouter',
},
},
},
},
'hct-partner-notification-dashboard-slot': {
Expand Down Expand Up @@ -282,36 +250,6 @@ export const mockContentPackages = [
},
],
},
'Translation overrides': {
en: {
partnerNotification: 'Partner Notification',
contactTracing: 'Contact Tracing',
contactDateTitle: 'Contact Date',
nameTitle: 'Name',
relationshipTitle: 'Relationship',
status: 'Status',
contactMethodTitle: 'Contact Method',
contactOutcomeTitle: 'Contact Outcome',
actionsTitle: 'Actions',
viewDetails: 'View Details',
editForm: 'Edit Form',
add: 'Add',
},
fr: {
partnerNotification: 'Notification des partenaires',
contactTracing: 'Recherche des contacts',
contactDateTitle: 'Date de contact',
nameTitle: 'Nom',
relationshipTitle: 'Relation',
status: 'Statut',
contactMethodTitle: 'Méthode de contact',
contactOutcomeTitle: 'Résultat du contact',
actionsTitle: 'Actions',
viewDetails: 'Voir les détails',
editForm: 'Modifier le formulaire',
add: 'Ajouter',
},
},
},
},
},
Expand Down
76 changes: 76 additions & 0 deletions src/components/dashboard/dashboard.component.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import React from 'react';
import { render, screen, waitFor } from '@testing-library/react';
import '@testing-library/jest-dom';
import { ContentPackagesList } from './dashboard.component';
import { getAllPackagesFromLocalStorage } from '../../utils';
import { mockContentPackages } from '../../../__mocks__/content-packages.mock';
import userEvent from '@testing-library/user-event';

jest.mock('../../utils', () => ({
getAllPackagesFromLocalStorage: jest.fn(),
}));

const mockSetClinicalViews = jest.fn();

describe('DashboardComponent', () => {
beforeEach(() => {
(getAllPackagesFromLocalStorage as jest.Mock).mockReturnValue(mockContentPackages);
});

it('renders the empty Dashboard component if there no clinical views', async () => {
render(<ContentPackagesList t={(key) => key} clinicalViews={[]} setClinicalViews={mockSetClinicalViews} />);
expect(screen.getByText('clinicalViewsTableHeader')).toBeInTheDocument();
await waitFor(() => {
expect(screen.getByText('Filter table')).toBeInTheDocument();
});
expect(screen.getByText('noMatchingClinicalViewsToDisplay')).toBeInTheDocument();
expect(screen.getByText('checkFilters')).toBeInTheDocument();
});

it('renders the clinical views if they exisit in the Dashboard component', async () => {
render(
<ContentPackagesList
t={(key) => key}
clinicalViews={mockContentPackages}
setClinicalViews={mockSetClinicalViews}
/>,
);

expect(screen.getByText('clinicalViewsTableHeader'));
await waitFor(() => {
expect(screen.getByText('Filter table')).toBeInTheDocument();
});

expect(screen.getByRole('table')).toBeInTheDocument();
expect(screen.getByRole('columnheader', { name: /name/i })).toBeInTheDocument();
expect(screen.getByRole('columnheader', { name: /dashboards/i })).toBeInTheDocument();
expect(screen.getByRole('columnheader', { name: /schemaActions/i })).toBeInTheDocument();

expect(screen.getByRole('row', { name: /hIVCareAndTreatment/i })).toBeInTheDocument();
expect(
screen.getByRole('row', {
name: /patientSummary, programManagement, clinicalVisits, generalCounselling, partnerNotification/i,
}),
).toBeInTheDocument();
expect(screen.getByRole('button', { name: /editSchema/i })).toBeInTheDocument();
expect(screen.getByRole('button', { name: /downloadSchema/i })).toBeInTheDocument();
expect(screen.getByRole('button', { name: /deleteSchema/i })).toBeInTheDocument();
});

it('handles search input', async () => {
render(
<ContentPackagesList
t={(key) => key}
clinicalViews={mockContentPackages}
setClinicalViews={mockSetClinicalViews}
/>,
);
await waitFor(() => {
expect(screen.getByText('clinicalViewsTableHeader'));
});

const searchInput = screen.getByLabelText('Filter table');
await userEvent.type(searchInput, 'Clinical View 1');
expect(searchInput).toHaveValue('Clinical View 1');
});
});
48 changes: 24 additions & 24 deletions src/components/dashboard/dashboard.component.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useCallback, useMemo, useState, useEffect } from 'react';
import React, { useCallback, useMemo, useState, useEffect, type Dispatch, type SetStateAction } from 'react';
import { useTranslation, type TFunction } from 'react-i18next';
import {
Button,
Expand All @@ -22,9 +22,9 @@ import { ContentPackagesBuilderPagination } from '../pagination';

import Header from '../header/header.component';
import styles from './dashboard.scss';
import { mockContentPackages } from '../../../__mocks__/content-packages.mock';
import { navigate, useLayoutType, usePagination, ConfigurableLink, showModal } from '@openmrs/esm-framework';
import { getAllPackagesFromLocalStorage, deletePackageFromLocalStorage } from '../../utils';
import { type ContentPackage } from '../../types';

interface ActionButtonsProps {
responsiveSize: string;
Expand Down Expand Up @@ -99,21 +99,21 @@ function ActionButtons({ responsiveSize, clinicalViewKey, t, onDelete, onDownloa
);
}

function ContentPackagesList({ isValidating, t }: any) {
interface ContentPackagesListProps {
t: TFunction;
clinicalViews: ContentPackage[];
setClinicalViews: Dispatch<SetStateAction<any[]>>;
}

export function ContentPackagesList({ t, clinicalViews, setClinicalViews }: ContentPackagesListProps) {
const pageSize = 10;
const isTablet = useLayoutType() === 'tablet';
const responsiveSize = isTablet ? 'lg' : 'sm';
const [searchString, setSearchString] = useState('');
const [clinicalViews, setClinicalViews] = useState<any[]>([]);

useEffect(() => {
const packages = getAllPackagesFromLocalStorage();
setClinicalViews(Object.entries(packages).map(([key, value]) => ({ key, ...(value as object) })));
}, []);

const filteredViews = useMemo(() => {
const searchTerm = searchString.trim().toLowerCase();
return clinicalViews.filter((pkg) => pkg.key.toLowerCase().includes(searchTerm));
return clinicalViews?.filter((pkg) => Object.keys(pkg)[0]?.toLowerCase().includes(searchTerm));
}, [clinicalViews, searchString]);

const handleEdit = (packageKey: string) => {
Expand All @@ -124,7 +124,7 @@ function ContentPackagesList({ isValidating, t }: any) {

const handleDelete = (packageKey: string) => {
deletePackageFromLocalStorage(packageKey);
setClinicalViews(clinicalViews.filter((pkg) => pkg.key !== packageKey));
setClinicalViews(clinicalViews?.filter((pkg) => Object.keys(pkg)[0] !== packageKey));
};

const tableHeaders = [
Expand Down Expand Up @@ -196,14 +196,15 @@ function ContentPackagesList({ isValidating, t }: any) {
const tableRows = results.map((contentPackage) => {
const clinicalViewName = getNavGroupTitle(contentPackage);
const dashboardTitles = getDashboardTitles(contentPackage);
const key = Object.keys(contentPackage)[0];

return {
id: contentPackage.key,
name: (
<ConfigurableLink
className={styles.link}
to={`${window.spaBase}/clinical-views-builder/edit/${contentPackage.id}`}
templateParams={{ clinicalViewUuid: contentPackage.id }}
templateParams={{ clinicalViewUuid: key }}
>
{clinicalViewName}
</ConfigurableLink>
Expand All @@ -212,11 +213,11 @@ function ContentPackagesList({ isValidating, t }: any) {
actions: (
<ActionButtons
responsiveSize={responsiveSize}
clinicalViewKey={contentPackage.key}
clinicalViewKey={key}
t={t}
onEdit={() => handleEdit(contentPackage.key)}
onDelete={() => handleDelete(contentPackage.key)}
onDownload={() => handleDownload(contentPackage.key)}
onEdit={() => handleEdit(key)}
onDelete={() => handleDelete(key)}
onDownload={() => handleDownload(key)}
/>
),
};
Expand All @@ -232,11 +233,6 @@ function ContentPackagesList({ isValidating, t }: any) {

return (
<>
<div className={styles.flexContainer}>
<div className={styles.backgroundDataFetchingIndicator}>
<span>{isValidating ? <InlineLoading /> : null}</span>
</div>
</div>
<div className={styles.tableHeading}>{t('clinicalViewsTableHeader', 'Clinical Views List')}</div>
<DataTable rows={tableRows} headers={tableHeaders} size={isTablet ? 'lg' : 'sm'} useZebraStyles>
{({ rows, headers, getTableProps, getHeaderProps, getRowProps }) => (
Expand Down Expand Up @@ -318,15 +314,19 @@ function ContentPackagesList({ isValidating, t }: any) {

const Dashboard: React.FC = () => {
const { t } = useTranslation();
const [clinicalViews, setClinicalViews] = useState<any[]>([]);

useEffect(() => {
const packages = getAllPackagesFromLocalStorage();
setClinicalViews(Object.entries(packages).map(([key, value]) => ({ key, ...(value as object) })));
}, []);

return (
<main>
<Header title={t('home', 'Home')} />
<div className={styles.container}>
{(() => {
return (
<ContentPackagesList contentPackages={mockContentPackages} isValidating={false} mutate={() => {}} t={t} />
);
return <ContentPackagesList t={t} clinicalViews={clinicalViews} setClinicalViews={setClinicalViews} />;
})()}
</div>
</main>
Expand Down
12 changes: 8 additions & 4 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,16 @@ export interface SlotConfiguration {
configure?: {
[key: string]:
| {
title: string;
title?: string;
slotName?: string;
isExpanded?: boolean;
path?: string;
tabDefinitions?: TabDefinition[];
'Translation overrides'?: TranslationOverrides;
}
| DashboardConfig;
};
'Translation overrides': TranslationOverrides;
'Translation overrides'?: TranslationOverrides;
}

interface ExtensionSlots {
Expand All @@ -43,7 +45,7 @@ export interface ActionOption {
export interface Column {
id: string;
title: string;
concept: string;
concept?: string;
isDate?: boolean;
isLink?: boolean;
type?: string;
Expand All @@ -54,8 +56,9 @@ export interface Column {
}

export interface TabDefinition {
id: any;
id?: any;
tabName: string;
title?: string;
headerTitle: string;
displayText: string;
encounterType: string;
Expand All @@ -82,6 +85,7 @@ export interface ExtensionSlot {
isExpanded?: boolean;
tabDefinitions?: TabDefinition[];
tilesDefinitions?: TilesDefinition[];
'Translation overrides'?: TranslationOverrides;
};
};
}
Expand Down
2 changes: 1 addition & 1 deletion src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export const getAllPackagesFromLocalStorage = () => {
const packages: any = {};
for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i);
if (key && key.startsWith('packageJSON_')) {
if (key && key.startsWith('@openmrs/esm-patient-chart-app')) {
// Ensure to only get relevant packages
packages[key] = JSON.parse(localStorage.getItem(key)!);
}
Expand Down

0 comments on commit 9ce5d8a

Please sign in to comment.