import React from 'react';
import { render, screen, within } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { formatDatetime, parseDate } from '@openmrs/esm-framework';
import { useLaunchWorkspaceRequiringVisit } from '@openmrs/esm-patient-common-lib';
import EncounterDetails from './encounter-details.component';
import { usePatientEncounter, useClinicalEncounterForm } from '../../../hooks/usePatientEncounter';
// Create a mock function for the workspace launcher
const mockLaunchWorkspaceRequiringVisit = jest.fn();
// Mock dependencies
jest.mock('@openmrs/esm-framework', () => ({
...jest.requireActual('@openmrs/esm-framework'),
formatDatetime: jest.fn((date, options) => '15-Jan-2024'),
parseDate: jest.fn((date) => new Date(date)),
openmrsFetch: jest.fn(),
useConfig: jest.fn(() => ({
clinicalEncounter: {
encounterTypeUuid: '465a92f2-baf8-42e9-9612-53064be868e8',
formUuid: 'e958f902-64df-4819-afd4-7fb061f59308',
},
})),
ErrorState: jest.fn(({ error, headerTitle }) => (
{headerTitle}
{error.message}
)),
}));
jest.mock('@openmrs/esm-patient-common-lib', () => ({
CardHeader: ({ title, children }: { title: string; children: React.ReactNode }) => (
{title}
{children}
),
useLaunchWorkspaceRequiringVisit: jest.fn(),
}));
jest.mock('@carbon/react', () => ({
...jest.requireActual('@carbon/react'),
TabsSkeleton: jest.fn(() => Loading tabs...
),
}));
jest.mock('../../../hooks/usePatientEncounter');
const mockUsePatientEncounter = usePatientEncounter as jest.MockedFunction;
const mockUseClinicalEncounterForm = useClinicalEncounterForm as jest.MockedFunction;
const mockUseLaunchWorkspaceRequiringVisit = useLaunchWorkspaceRequiringVisit as jest.MockedFunction<
typeof useLaunchWorkspaceRequiringVisit
>;
describe('EncounterDetails', () => {
const patientUuid = 'test-patient-uuid';
const mockEncounters = [
{
uuid: 'encounter-1',
encounterDatetime: '2024-01-15T10:30:00.000Z',
encounterType: {
uuid: 'encounter-type-uuid',
display: 'Clinical Consultation',
},
location: {
uuid: 'location-uuid',
display: 'Outpatient Clinic',
},
encounterProviders: [
{
uuid: 'provider-uuid',
display: 'Dr. John Doe',
provider: {
uuid: 'provider-uuid',
display: 'Dr. John Doe',
},
},
],
obs: [
{
uuid: 'obs-1',
concept: {
uuid: 'concept-visit-uuid',
display: 'Visit Reason',
},
value: 'Follow-up',
obsDatetime: '2024-01-15T10:30:00.000Z',
},
{
uuid: 'obs-2',
concept: {
uuid: 'concept-history-uuid',
display: 'Chief Complaint',
},
value: 'Headache',
obsDatetime: '2024-01-15T10:30:00.000Z',
},
{
uuid: 'obs-3',
concept: {
uuid: 'concept-exam-uuid',
display: 'Blood Pressure',
},
value: '120/80',
obsDatetime: '2024-01-15T10:30:00.000Z',
},
{
uuid: 'obs-4',
concept: {
uuid: 'concept-management-uuid',
display: 'Diagnosis',
},
value: {
display: 'Migraine',
},
obsDatetime: '2024-01-15T10:30:00.000Z',
},
],
},
{
uuid: 'encounter-2',
encounterDatetime: '2024-01-10T09:00:00.000Z',
encounterType: {
uuid: 'encounter-type-uuid',
display: 'Clinical Consultation',
},
location: {
uuid: 'location-uuid',
display: 'Outpatient Clinic',
},
encounterProviders: [],
obs: [],
},
];
const mockConceptLabelMap = {
'Visit Details': {
'concept-visit-uuid': 'Visit Reason',
},
'Patient History': {
'concept-history-uuid': 'Chief Complaint',
},
'Patient Examination': {
'concept-exam-uuid': 'Blood Pressure',
},
'Patient Management': {
'concept-management-uuid': 'Diagnosis',
},
};
beforeEach(() => {
jest.clearAllMocks();
mockLaunchWorkspaceRequiringVisit.mockClear();
mockUseLaunchWorkspaceRequiringVisit.mockReturnValue(mockLaunchWorkspaceRequiringVisit);
mockUsePatientEncounter.mockReturnValue({
encounters: mockEncounters as Array,
isLoading: false,
error: null,
mutate: jest.fn(),
});
mockUseClinicalEncounterForm.mockReturnValue({
form: {},
conceptLabelMap: mockConceptLabelMap,
isLoading: false,
error: null,
mutate: jest.fn(),
});
});
describe('Component Rendering', () => {
it('renders the component with tabs', () => {
render();
const tabs = screen.getAllByRole('tab');
expect(tabs).toHaveLength(5);
expect(screen.getAllByText('Visit Details').length).toBeGreaterThan(0);
expect(screen.getAllByText('Patient History').length).toBeGreaterThan(0);
expect(screen.getAllByText('Patient Examination').length).toBeGreaterThan(0);
expect(screen.getAllByText('Patient Management').length).toBeGreaterThan(0);
expect(screen.getAllByText('Lab Results').length).toBeGreaterThan(0);
});
it('renders with correct patient UUID prop', () => {
render();
expect(mockUsePatientEncounter).toHaveBeenCalledWith(patientUuid, '465a92f2-baf8-42e9-9612-53064be868e8');
});
it('displays the most recent encounter', () => {
render();
// The component should display the most recent encounter (encounter-1 from 2024-01-15)
expect(screen.getByText('15-Jan-2024')).toBeInTheDocument();
expect(screen.getByText('Clinical Consultation')).toBeInTheDocument();
expect(screen.getByText('Outpatient Clinic')).toBeInTheDocument();
expect(screen.getByText('Dr. John Doe')).toBeInTheDocument();
});
});
describe('Visit Details Tab', () => {
it('displays visit details correctly', () => {
render();
// Visit Details tab should be visible by default
expect(screen.getByText('Visit Date')).toBeInTheDocument();
expect(screen.getByText('Visit Type')).toBeInTheDocument();
expect(screen.getByText('Visit Location')).toBeInTheDocument();
expect(screen.getByText('Visit Provider')).toBeInTheDocument();
});
it('renders Add button in Visit Details', () => {
render();
const addButtons = screen.getAllByText('Add');
expect(addButtons.length).toBeGreaterThan(0);
});
});
describe('Tab Navigation', () => {
it('switches to Patient History tab and displays correct content', async () => {
const user = userEvent.setup();
render();
const historyTab = screen.getAllByText('Patient History')[0];
await user.click(historyTab);
// Check that Patient History tab is active and renders card header
const tabPanel = screen.getByRole('tabpanel');
expect(within(tabPanel).getByText('Chief Complaint')).toBeInTheDocument();
});
it('switches to Patient Examination tab and displays correct content', async () => {
const user = userEvent.setup();
render();
const examinationTab = screen.getAllByText('Patient Examination')[0];
await user.click(examinationTab);
// Check that Patient Examination tab is active and renders card header
const tabPanel = screen.getByRole('tabpanel');
expect(within(tabPanel).getByText('Blood Pressure')).toBeInTheDocument();
});
it('switches to Patient Management tab and displays correct content', async () => {
const user = userEvent.setup();
render();
const managementTab = screen.getAllByText('Patient Management')[0];
await user.click(managementTab);
// Check that Patient Management tab is active and renders card header
const tabPanel = screen.getByRole('tabpanel');
expect(within(tabPanel).getByText('Diagnosis')).toBeInTheDocument();
});
});
describe('Add Button Functionality', () => {
it('launches workspace when Add button is clicked', async () => {
const user = userEvent.setup();
render();
const addButton = screen.getAllByText('Add')[0];
await user.click(addButton);
expect(mockLaunchWorkspaceRequiringVisit).toHaveBeenCalledWith({
workspaceTitle: 'Visit Details',
mutateForm: expect.any(Function),
formInfo: {
encounterUuid: '',
formUuid: 'e958f902-64df-4819-afd4-7fb061f59308',
additionalProps: {},
},
});
});
it('launches workspace with correct title for Patient History tab', async () => {
const user = userEvent.setup();
render();
// Switch to Patient History tab
const tabs = screen.getAllByRole('tab');
await user.click(tabs[1]); // Click Patient History tab
// Wait for tab panel to be visible and find the Add button within it
const tabPanel = screen.getByRole('tabpanel');
const addButton = within(tabPanel).getByText('Add');
await user.click(addButton);
expect(mockLaunchWorkspaceRequiringVisit).toHaveBeenCalledWith(
expect.objectContaining({
workspaceTitle: 'Patient History',
}),
);
});
});
describe('Loading and Error States', () => {
it('renders skeleton when loading', () => {
mockUsePatientEncounter.mockReturnValue({
encounters: [],
isLoading: true,
error: null,
mutate: jest.fn(),
});
render();
// Component should render loading skeleton
expect(screen.getByTestId('tabs-skeleton')).toBeInTheDocument();
expect(screen.getByText('Loading tabs...')).toBeInTheDocument();
});
it('renders error state when there is an error', () => {
const errorMessage = 'Failed to fetch encounters';
mockUsePatientEncounter.mockReturnValue({
encounters: [],
isLoading: false,
error: new Error(errorMessage),
mutate: jest.fn(),
});
render();
// Component should render error state
expect(screen.getByTestId('error-state')).toBeInTheDocument();
expect(screen.getByText('Encounter Details')).toBeInTheDocument();
expect(screen.getByText(errorMessage)).toBeInTheDocument();
});
});
describe('Edge Cases', () => {
it('handles empty encounters array', () => {
mockUsePatientEncounter.mockReturnValue({
encounters: [],
isLoading: false,
error: null,
mutate: jest.fn(),
});
render();
expect(screen.getAllByText('Visit Details').length).toBeGreaterThan(0);
// Should not crash when there are no encounters
});
it('handles encounter with no observations', () => {
mockUsePatientEncounter.mockReturnValue({
encounters: [
{
uuid: 'encounter-no-obs',
encounterDatetime: '2024-01-15T10:30:00.000Z',
encounterType: {
uuid: 'encounter-type-uuid',
display: 'Clinical Consultation',
},
location: {
uuid: 'location-uuid',
display: 'Outpatient Clinic',
},
encounterProviders: [],
obs: [],
},
],
isLoading: false,
error: null,
mutate: jest.fn(),
});
render();
expect(screen.getAllByText('Visit Details').length).toBeGreaterThan(0);
// Should render basic encounter info even without observations
expect(screen.getByText('Clinical Consultation')).toBeInTheDocument();
});
it('handles missing encounter providers', () => {
mockUsePatientEncounter.mockReturnValue({
encounters: [
{
uuid: 'encounter-no-provider',
encounterDatetime: '2024-01-15T10:30:00.000Z',
encounterType: {
uuid: 'encounter-type-uuid',
display: 'Clinical Consultation',
},
location: {
uuid: 'location-uuid',
display: 'Outpatient Clinic',
},
encounterProviders: [],
obs: [],
},
],
isLoading: false,
error: null,
mutate: jest.fn(),
});
render();
expect(screen.getByText('Visit Provider')).toBeInTheDocument();
// Should handle undefined provider gracefully
});
it('handles null conceptLabelMap', () => {
mockUseClinicalEncounterForm.mockReturnValue({
form: {},
conceptLabelMap: null,
isLoading: false,
error: null,
mutate: jest.fn(),
});
render();
expect(screen.getAllByText('Visit Details').length).toBeGreaterThan(0);
// Should not crash with null conceptLabelMap
});
it('handles observations with unmapped concepts', () => {
mockUsePatientEncounter.mockReturnValue({
encounters: [
{
uuid: 'encounter-unmapped',
encounterDatetime: '2024-01-15T10:30:00.000Z',
encounterType: {
uuid: 'encounter-type-uuid',
display: 'Clinical Consultation',
},
location: {
uuid: 'location-uuid',
display: 'Outpatient Clinic',
},
encounterProviders: [],
obs: [
{
uuid: 'obs-unmapped',
concept: {
uuid: 'unmapped-concept-uuid',
display: 'Unknown Concept',
},
value: 'Some value',
obsDatetime: '2024-01-15T10:30:00.000Z',
},
],
},
],
isLoading: false,
error: null,
mutate: jest.fn(),
});
render();
expect(screen.getAllByText('Visit Details').length).toBeGreaterThan(0);
// Unmapped observations should not appear in any section
});
});
describe('Observation Grouping', () => {
it('groups observations correctly by section', async () => {
const user = userEvent.setup();
render();
// Component should render tabs
const tabs = screen.getAllByRole('tab');
expect(tabs).toHaveLength(5);
// Check Patient History section
const historyTab = tabs[1];
await user.click(historyTab);
let tabPanel = screen.getByRole('tabpanel');
expect(within(tabPanel).getByText('Chief Complaint')).toBeInTheDocument();
// Check Patient Examination section
const examinationTab = tabs[2];
await user.click(examinationTab);
tabPanel = screen.getByRole('tabpanel');
expect(within(tabPanel).getByText('Blood Pressure')).toBeInTheDocument();
// Check Patient Management section
const managementTab = tabs[3];
await user.click(managementTab);
tabPanel = screen.getByRole('tabpanel');
expect(within(tabPanel).getByText('Diagnosis')).toBeInTheDocument();
});
});
describe('Encounter Sorting', () => {
it('displays the most recent encounter when multiple encounters exist', () => {
const multipleEncounters = [
{
...mockEncounters[1],
encounterDatetime: '2024-01-10T09:00:00.000Z', // Older
},
{
...mockEncounters[0],
encounterDatetime: '2024-01-20T14:00:00.000Z', // Newer
},
];
mockUsePatientEncounter.mockReturnValue({
encounters: multipleEncounters as Array,
isLoading: false,
error: null,
mutate: jest.fn(),
});
render();
// Should display the newer encounter date
expect(formatDatetime).toHaveBeenCalledWith(expect.any(Date), { mode: 'wide', noToday: true });
});
});
describe('Accessibility', () => {
it('maintains proper tab navigation', async () => {
const user = userEvent.setup();
render();
const tabs = screen.getAllByRole('tab');
expect(tabs).toHaveLength(5);
// All tabs should be accessible
tabs.forEach((tab) => {
expect(tab).toBeInTheDocument();
});
});
it('has proper ARIA attributes for tabs', () => {
render();
const tabs = screen.getAllByRole('tab');
tabs.forEach((tab) => {
expect(tab).toHaveAttribute('aria-selected');
});
});
});
});