Skip to main content
Glama
LabOrderDetails.test.tsx25.7 kB
// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors // SPDX-License-Identifier: Apache-2.0 import { MantineProvider } from '@mantine/core'; import { act, render, screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { MedplumProvider } from '@medplum/react'; import type { Patient, ServiceRequest, Practitioner, DiagnosticReport, DocumentReference, QuestionnaireResponse, Observation, } from '@medplum/fhirtypes'; import { MockClient } from '@medplum/mock'; import { MemoryRouter } from 'react-router'; import { describe, expect, test, vi, beforeEach } from 'vitest'; import { LabOrderDetails } from './LabOrderDetails'; // Mock the documentReference utility vi.mock('../../utils/documentReference', () => ({ fetchLabOrderRequisitionDocuments: vi.fn(), getHealthGorillaRequisitionId: vi.fn(), })); const mockPatient: Patient = { resourceType: 'Patient', id: 'patient-123', name: [{ given: ['John'], family: 'Doe' }], gender: 'male', birthDate: '1990-01-01', }; const mockPractitioner: Practitioner = { resourceType: 'Practitioner', id: 'practitioner-123', name: [{ given: ['Dr. Jane'], family: 'Smith' }], }; const mockActiveServiceRequest: ServiceRequest = { resourceType: 'ServiceRequest', id: 'service-request-123', status: 'active', intent: 'order', code: { coding: [ { system: 'http://loinc.org', code: '24323-8', display: 'Complete Blood Count', }, ], text: 'CBC with Differential', }, subject: { reference: 'Patient/patient-123', display: 'John Doe', }, requester: { reference: 'Practitioner/practitioner-123', display: 'Dr. Jane Smith', }, authoredOn: '2024-01-15T10:00:00Z', priority: 'routine', reasonCode: [ { text: 'Annual physical exam', }, ], note: [ { text: 'Patient is fasting', }, ], performer: [ { display: 'Quest Diagnostics', }, ], requisition: { system: 'https://www.healthgorilla.com', value: 'HG-REQ-12345', }, }; const mockCompletedServiceRequest: ServiceRequest = { ...mockActiveServiceRequest, status: 'completed', meta: { lastUpdated: '2024-01-20T14:30:00Z', }, }; const mockDiagnosticReport: DiagnosticReport = { resourceType: 'DiagnosticReport', id: 'diagnostic-report-123', status: 'final', code: { coding: [ { system: 'http://loinc.org', code: '24323-8', display: 'Complete Blood Count', }, ], }, subject: { reference: 'Patient/patient-123', }, effectiveDateTime: '2024-01-18T09:00:00Z', issued: '2024-01-19T15:00:00Z', basedOn: [ { reference: 'ServiceRequest/service-request-123', }, ], result: [ { reference: 'Observation/observation-123', }, ], conclusion: 'All values within normal range', presentedForm: [ { contentType: 'application/pdf', url: 'https://example.com/report.pdf', title: 'Lab Report', }, ], }; const mockObservation: Observation = { resourceType: 'Observation', id: 'observation-123', status: 'final', code: { coding: [ { system: 'http://loinc.org', code: '6690-2', display: 'White Blood Cell Count', }, ], }, subject: { reference: 'Patient/patient-123', }, valueQuantity: { value: 7.5, unit: 'x10^3/uL', system: 'http://unitsofmeasure.org', }, }; const mockLabOrderRequisitionDoc: DocumentReference = { resourceType: 'DocumentReference', id: 'doc-ref-123', status: 'current', type: { coding: [ { code: 'LabOrderRequisition', display: 'Lab Order Requisition', }, ], }, subject: { reference: 'Patient/patient-123', }, content: [ { attachment: { contentType: 'application/pdf', url: 'https://example.com/requisition.pdf', title: 'Lab Requisition', }, }, ], identifier: [ { system: 'https://www.healthgorilla.com', value: 'HG-REQ-12345', }, ], }; const mockSpecimenLabelDoc: DocumentReference = { resourceType: 'DocumentReference', id: 'doc-ref-456', status: 'current', type: { coding: [ { code: 'SpecimenLabel', display: 'Specimen Label', }, ], }, subject: { reference: 'Patient/patient-123', }, content: [ { attachment: { contentType: 'application/pdf', url: 'https://example.com/specimen-label.pdf', title: 'Specimen Label', }, }, ], identifier: [ { system: 'https://www.healthgorilla.com', value: 'HG-REQ-12345', }, ], }; const mockQuestionnaireResponse: QuestionnaireResponse = { resourceType: 'QuestionnaireResponse', id: 'questionnaire-response-123', status: 'completed', authored: '2024-01-15T09:30:00Z', item: [ { linkId: 'question-1', text: 'Is the patient fasting?', answer: [ { valueBoolean: true, }, ], }, { linkId: 'question-2', text: 'Current medications', answer: [ { valueString: 'Aspirin 81mg daily', }, ], }, ], }; describe('LabOrderDetails', () => { let medplum: MockClient; let fetchLabOrderRequisitionDocumentsMock: any; let getHealthGorillaRequisitionIdMock: any; beforeEach(async () => { medplum = new MockClient(); vi.clearAllMocks(); const documentReferenceUtils = await import('../../utils/documentReference'); fetchLabOrderRequisitionDocumentsMock = vi.mocked(documentReferenceUtils.fetchLabOrderRequisitionDocuments); getHealthGorillaRequisitionIdMock = vi.mocked(documentReferenceUtils.getHealthGorillaRequisitionId); // Setup default mocks fetchLabOrderRequisitionDocumentsMock.mockResolvedValue([]); getHealthGorillaRequisitionIdMock.mockReturnValue('HG-REQ-12345'); medplum.searchResources = vi.fn().mockResolvedValue([]); }); const setup = (props: Partial<Parameters<typeof LabOrderDetails>[0]> = {}): ReturnType<typeof render> => { return render( <MemoryRouter> <MedplumProvider medplum={medplum}> <MantineProvider> <LabOrderDetails order={mockActiveServiceRequest} {...props} /> </MantineProvider> </MedplumProvider> </MemoryRouter> ); }; describe('Basic rendering', () => { test('renders order details with active status', async () => { medplum.readResource = vi.fn().mockImplementation((resourceType: string, id: string) => { if (resourceType === 'Patient' && id === 'patient-123') { return Promise.resolve(mockPatient); } if (resourceType === 'Practitioner' && id === 'practitioner-123') { return Promise.resolve(mockPractitioner); } return Promise.reject(new Error('Not found')); }); await act(async () => { setup(); }); await waitFor(() => { expect(screen.getByText('CBC with Differential')).toBeInTheDocument(); }); expect(screen.getByText(/Ordered/i)).toBeInTheDocument(); expect(screen.getByText('Active')).toBeInTheDocument(); }); test('renders order details with completed status', async () => { medplum.readResource = vi.fn().mockResolvedValue(mockPatient); await act(async () => { setup({ order: mockCompletedServiceRequest }); }); await waitFor(() => { expect(screen.getByText('CBC with Differential')).toBeInTheDocument(); }); expect(screen.getByText('Completed')).toBeInTheDocument(); }); test('displays multiple test codes', async () => { const orderWithMultipleCodes: ServiceRequest = { ...mockActiveServiceRequest, code: { coding: [ { system: 'http://loinc.org', code: '24323-8', display: 'Complete Blood Count', }, { system: 'http://loinc.org', code: '2345-7', display: 'Glucose', }, ], }, }; await act(async () => { setup({ order: orderWithMultipleCodes }); }); await waitFor(() => { expect(screen.getByText(/Complete Blood Count, Glucose/)).toBeInTheDocument(); }); }); test('falls back to first code display when no text field', async () => { const orderWithoutText: ServiceRequest = { ...mockActiveServiceRequest, code: { coding: [ { system: 'http://loinc.org', code: '24323-8', display: 'Complete Blood Count', }, ], }, }; await act(async () => { setup({ order: orderWithoutText }); }); await waitFor(() => { expect(screen.getByText('Complete Blood Count')).toBeInTheDocument(); }); }); }); describe('Tab switching', () => { test('defaults to progress tracker for active orders', async () => { await act(async () => { setup({ order: mockActiveServiceRequest }); }); await waitFor(() => { const progressButton = screen.getByText('Progress Tracker'); expect(progressButton).toBeInTheDocument(); }); }); test('defaults to report tab for completed orders', async () => { medplum.searchResources = vi.fn().mockResolvedValue([mockDiagnosticReport]); await act(async () => { setup({ order: mockCompletedServiceRequest }); }); await waitFor(() => { const reportButton = screen.getByText('Report'); expect(reportButton).toBeInTheDocument(); }); }); test('switches to order details tab when clicked', async () => { const user = userEvent.setup(); medplum.readResource = vi.fn().mockResolvedValue(mockPractitioner); await act(async () => { setup(); }); await waitFor(() => { expect(screen.getByText('Progress Tracker')).toBeInTheDocument(); }); const orderDetailsButton = screen.getByText('Order Details'); await user.click(orderDetailsButton); await waitFor(() => { expect(screen.getByText('Order Date')).toBeInTheDocument(); expect(screen.getByText('Test Code')).toBeInTheDocument(); }); }); }); describe('Order details tab', () => { beforeEach(() => { medplum.readResource = vi.fn().mockImplementation((resourceType: string, id: string) => { if (resourceType === 'Patient' && id === 'patient-123') { return Promise.resolve(mockPatient); } if (resourceType === 'Practitioner' && id === 'practitioner-123') { return Promise.resolve(mockPractitioner); } return Promise.reject(new Error('Not found')); }); }); test('displays order information correctly', async () => { const user = userEvent.setup(); await act(async () => { setup(); }); const orderDetailsButton = screen.getByText('Order Details'); await user.click(orderDetailsButton); await waitFor(() => { expect(screen.getByText('Order Date')).toBeInTheDocument(); expect(screen.getByText('Test Code')).toBeInTheDocument(); expect(screen.getByText('Ordering provider')).toBeInTheDocument(); expect(screen.getByText('Performing lab')).toBeInTheDocument(); expect(screen.getByText('Requisition ID')).toBeInTheDocument(); expect(screen.getByText('Patient')).toBeInTheDocument(); expect(screen.getByText('Priority')).toBeInTheDocument(); }); expect(screen.getByText('Dr. Jane Smith')).toBeInTheDocument(); expect(screen.getByText('John Doe')).toBeInTheDocument(); expect(screen.getByText('Quest Diagnostics')).toBeInTheDocument(); expect(screen.getByText('HG-REQ-12345')).toBeInTheDocument(); expect(screen.getByText('routine')).toBeInTheDocument(); }); test('displays reason code and notes', async () => { const user = userEvent.setup(); await act(async () => { setup(); }); const orderDetailsButton = screen.getByText('Order Details'); await user.click(orderDetailsButton); await waitFor(() => { expect(screen.getByText('Reason')).toBeInTheDocument(); expect(screen.getByText('Annual physical exam')).toBeInTheDocument(); expect(screen.getByText('Notes')).toBeInTheDocument(); expect(screen.getByText('Patient is fasting')).toBeInTheDocument(); }); }); test('displays order detail if present', async () => { const user = userEvent.setup(); const orderWithDetails: ServiceRequest = { ...mockActiveServiceRequest, orderDetail: [ { text: 'Stat processing required', }, ], }; await act(async () => { setup({ order: orderWithDetails }); }); const orderDetailsButton = screen.getByRole('button', { name: 'Order Details' }); await user.click(orderDetailsButton); await waitFor(() => { expect(screen.getByText('Stat processing required')).toBeInTheDocument(); }); }); }); describe('Progress tracker', () => { test('displays progress tracker for active orders', async () => { await act(async () => { setup({ order: mockActiveServiceRequest }); }); await waitFor(() => { expect(screen.getByText('Order Sent')).toBeInTheDocument(); expect(screen.getByText('Order Acknowledged')).toBeInTheDocument(); expect(screen.getByText('Testing')).toBeInTheDocument(); expect(screen.getByText('Final')).toBeInTheDocument(); }); }); test('marks steps as completed based on available data', async () => { fetchLabOrderRequisitionDocumentsMock.mockResolvedValue([mockLabOrderRequisitionDoc]); medplum.searchResources = vi.fn().mockResolvedValue([mockDiagnosticReport]); await act(async () => { setup({ order: mockActiveServiceRequest }); }); await waitFor(() => { expect(screen.getByText('Order Sent')).toBeInTheDocument(); expect(screen.getByText('Order Acknowledged')).toBeInTheDocument(); }); }); test('shows timestamps for completed steps', async () => { await act(async () => { setup({ order: mockActiveServiceRequest }); }); await waitFor(() => { expect(screen.getByText('Order Sent')).toBeInTheDocument(); }); }); }); describe('Diagnostic report', () => { test('displays diagnostic report for completed orders', async () => { medplum.searchResources = vi.fn().mockResolvedValue([mockDiagnosticReport]); medplum.readResource = vi.fn().mockResolvedValue(mockObservation); await act(async () => { setup({ order: mockCompletedServiceRequest }); }); await waitFor( () => { expect(screen.getByText('Report Status')).toBeInTheDocument(); const finalStatuses = screen.getAllByText('final'); expect(finalStatuses.length).toBeGreaterThan(0); }, { timeout: 3000 } ); }); test('displays issue date', async () => { medplum.searchResources = vi.fn().mockResolvedValue([mockDiagnosticReport]); await act(async () => { setup({ order: mockCompletedServiceRequest }); }); await waitFor(() => { expect(screen.getByText('Issue Date')).toBeInTheDocument(); }); }); test('displays conclusion if present', async () => { medplum.searchResources = vi.fn().mockResolvedValue([mockDiagnosticReport]); await act(async () => { setup({ order: mockCompletedServiceRequest }); }); await waitFor( () => { const interpretations = screen.getAllByText('Interpretation'); expect(interpretations.length).toBeGreaterThan(0); expect(screen.getByText('All values within normal range')).toBeInTheDocument(); }, { timeout: 3000 } ); }); test('displays lab document section with presentedForm', async () => { medplum.searchResources = vi.fn().mockResolvedValue([mockDiagnosticReport]); await act(async () => { setup({ order: mockCompletedServiceRequest }); }); await waitFor(() => { expect(screen.getByText('Lab Document')).toBeInTheDocument(); }); }); }); describe('Document references', () => { test('fetches and displays lab order requisition documents', async () => { const user = userEvent.setup(); fetchLabOrderRequisitionDocumentsMock.mockResolvedValue([mockLabOrderRequisitionDoc]); await act(async () => { setup(); }); const orderDetailsButton = screen.getByText('Order Details'); await user.click(orderDetailsButton); await waitFor(() => { expect(fetchLabOrderRequisitionDocumentsMock).toHaveBeenCalledWith(medplum, mockActiveServiceRequest); expect(screen.getByText('Requisition Document')).toBeInTheDocument(); }); }); test('fetches and displays specimen label documents', async () => { const user = userEvent.setup(); medplum.searchResources = vi.fn().mockImplementation(async (resourceType: string, params: any) => { const searchParams = new URLSearchParams(params); if (resourceType === 'DocumentReference' && searchParams.get('category') === 'SpecimenLabel') { return [mockSpecimenLabelDoc]; } return []; }); await act(async () => { setup(); }); const orderDetailsButton = screen.getByRole('button', { name: 'Order Details' }); await user.click(orderDetailsButton); await waitFor( () => { const specimenLabels = screen.getAllByText('Specimen Label'); expect(specimenLabels.length).toBeGreaterThan(0); }, { timeout: 3000 } ); }); test('displays message when no specimen label documents found', async () => { const user = userEvent.setup(); await act(async () => { setup(); }); const orderDetailsButton = screen.getByText('Order Details'); await user.click(orderDetailsButton); await waitFor(() => { expect(screen.getByText('No specimen label documents found.')).toBeInTheDocument(); }); }); }); describe('Questionnaire response', () => { test('fetches and displays questionnaire response from current order', async () => { const user = userEvent.setup(); const orderWithQuestionnaire: ServiceRequest = { ...mockActiveServiceRequest, supportingInfo: [ { reference: 'QuestionnaireResponse/questionnaire-response-123', }, ], }; medplum.readResource = vi.fn().mockResolvedValue(mockQuestionnaireResponse); await act(async () => { setup({ order: orderWithQuestionnaire }); }); const orderDetailsButton = screen.getByText('Order Details'); await user.click(orderDetailsButton); await waitFor(() => { expect(screen.getByText('Order Entry Questions')).toBeInTheDocument(); expect(screen.getByText('Is the patient fasting?')).toBeInTheDocument(); expect(screen.getByText('true')).toBeInTheDocument(); expect(screen.getByText('Current medications')).toBeInTheDocument(); expect(screen.getByText('Aspirin 81mg daily')).toBeInTheDocument(); }); }); test('searches for questionnaire response from original order', async () => { const user = userEvent.setup(); const originalOrder: ServiceRequest = { ...mockActiveServiceRequest, id: 'original-order-123', supportingInfo: [ { reference: 'QuestionnaireResponse/questionnaire-response-123', }, ], basedOn: [ { reference: 'ServiceRequest/service-request-123', }, ], }; medplum.searchResources = vi.fn().mockResolvedValue([originalOrder]); medplum.readResource = vi.fn().mockResolvedValue(mockQuestionnaireResponse); await act(async () => { setup(); }); const orderDetailsButton = screen.getByText('Order Details'); await user.click(orderDetailsButton); await waitFor(() => { expect(medplum.searchResources).toHaveBeenCalledWith('ServiceRequest', expect.any(Object)); }); }); test('handles different answer types in questionnaire response', async () => { const user = userEvent.setup(); const questionnaireWithMultipleAnswerTypes: QuestionnaireResponse = { ...mockQuestionnaireResponse, item: [ { linkId: 'q1', text: 'Boolean question', answer: [{ valueBoolean: true }], }, { linkId: 'q2', text: 'String question', answer: [{ valueString: 'Answer text' }], }, { linkId: 'q3', text: 'Integer question', answer: [{ valueInteger: 42 }], }, { linkId: 'q4', text: 'Decimal question', answer: [{ valueDecimal: 3.14 }], }, { linkId: 'q5', text: 'Date question', answer: [{ valueDate: '2024-01-15' }], }, { linkId: 'q6', text: 'Coding question', answer: [{ valueCoding: { code: 'CODE-1', display: 'Display text' } }], }, { linkId: 'q7', text: 'Quantity question', answer: [{ valueQuantity: { value: 100, unit: 'mg' } }], }, ], }; const orderWithQuestionnaire: ServiceRequest = { ...mockActiveServiceRequest, supportingInfo: [ { reference: 'QuestionnaireResponse/questionnaire-response-123', }, ], }; medplum.readResource = vi.fn().mockResolvedValue(questionnaireWithMultipleAnswerTypes); await act(async () => { setup({ order: orderWithQuestionnaire }); }); const orderDetailsButton = screen.getByText('Order Details'); await user.click(orderDetailsButton); await waitFor(() => { expect(screen.getByText('Boolean question')).toBeInTheDocument(); expect(screen.getByText('true')).toBeInTheDocument(); expect(screen.getByText('String question')).toBeInTheDocument(); expect(screen.getByText('Answer text')).toBeInTheDocument(); expect(screen.getByText('Integer question')).toBeInTheDocument(); expect(screen.getByText('42')).toBeInTheDocument(); expect(screen.getByText('Decimal question')).toBeInTheDocument(); expect(screen.getByText('3.14')).toBeInTheDocument(); expect(screen.getByText('Date question')).toBeInTheDocument(); expect(screen.getByText('2024-01-15')).toBeInTheDocument(); expect(screen.getByText('Coding question')).toBeInTheDocument(); expect(screen.getByText('Display text')).toBeInTheDocument(); expect(screen.getByText('Quantity question')).toBeInTheDocument(); expect(screen.getByText('100 mg')).toBeInTheDocument(); }); }); }); describe('Status badges', () => { test.each([ ['active', 'Active'], ['completed', 'Completed'], ['draft', 'Draft'], ['requested', 'Requested'], ['on-hold', 'On Hold'], ['cancelled', 'Cancelled'], ['revoked', 'Revoked'], ['entered-in-error', 'Error'], ['unknown', 'Unknown'], ])('displays %s status correctly', async (status, expectedText) => { const orderWithStatus: ServiceRequest = { ...mockActiveServiceRequest, status: status as any, }; await act(async () => { setup({ order: orderWithStatus }); }); await waitFor(() => { expect(screen.getByText(expectedText)).toBeInTheDocument(); }); }); }); describe('Error handling', () => { test('handles error when fetching requisition documents fails', async () => { const user = userEvent.setup(); fetchLabOrderRequisitionDocumentsMock.mockRejectedValue(new Error('Fetch failed')); await act(async () => { setup(); }); const orderDetailsButton = screen.getByText('Order Details'); await user.click(orderDetailsButton); await waitFor(() => { expect(screen.getByText('No lab order requisition documents found.')).toBeInTheDocument(); }); }); }); describe('Cleanup', () => { test('cleans up documents when order changes', async () => { const { rerender } = await act(async () => { return setup(); }); await waitFor(() => { expect(fetchLabOrderRequisitionDocumentsMock).toHaveBeenCalledTimes(1); }); const newOrder: ServiceRequest = { ...mockActiveServiceRequest, id: 'new-order-456', }; await act(async () => { rerender( <MemoryRouter> <MedplumProvider medplum={medplum}> <MantineProvider> <LabOrderDetails order={newOrder} /> </MantineProvider> </MedplumProvider> </MemoryRouter> ); }); await waitFor(() => { expect(fetchLabOrderRequisitionDocumentsMock).toHaveBeenCalledWith(medplum, newOrder); }); }); }); });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/medplum/medplum'

If you have feedback or need assistance with the MCP directory API, please join our Discord server