Skip to main content
Glama
CcdaDisplay.test.tsx10.2 kB
// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors // SPDX-License-Identifier: Apache-2.0 import { MockClient } from '@medplum/mock'; import { MedplumProvider } from '@medplum/react-hooks'; import { act, fireEvent, render, screen, waitFor } from '../test-utils/render'; import * as domUtils from '../utils/dom'; import { CcdaDisplay } from './CcdaDisplay'; const EXAMPLE_CCDA_URL = 'http://example.com/ccda'; const VALIDATION_URL_PATTERN = 'https://ccda-validator.medplum.com/referenceccdaservice/'; describe('CcdaDisplay', () => { let medplum: MockClient; let fetchSpy: jest.Mock; let exportJsonFileSpy: jest.SpyInstance; // Keep original fetch const originalFetch = global.fetch; beforeAll(() => { // Mock the exportJsonFile function exportJsonFileSpy = jest.spyOn(domUtils, 'exportJsonFile').mockImplementation(jest.fn()); }); beforeEach(() => { medplum = new MockClient(); // Mock global fetch for both retrieving CCDA content and validation API fetchSpy = jest.fn().mockImplementation((url: string | URL, options?: RequestInit) => { const urlString = url.toString(); // For CCDA content retrieval (GET request) if (urlString === EXAMPLE_CCDA_URL && (!options?.method || options.method === 'GET')) { return Promise.resolve({ ok: true, status: 200, text: () => Promise.resolve('<ClinicalDocument>Example CCDA Content</ClinicalDocument>'), } as Response); } // For validation API (POST request) if (urlString.includes(VALIDATION_URL_PATTERN) && options?.method === 'POST') { const mockResponse = { resultsMetaData: { ccdaDocumentType: 'Care Plan', ccdaVersion: 'R2.1', objectiveProvided: 'C-CDA_IG_Plus_Vocab', serviceError: false, serviceErrorMessage: null, ccdaFileName: 'ccda.xml', ccdaFileContents: 'example content', resultMetaData: [ { type: 'C-CDA MDHT Conformance Error', count: 5, }, { type: 'C-CDA MDHT Conformance Warning', count: 3, }, { type: 'ONC 2015 S&CC Reference C-CDA Validation Error', count: 4, }, ], severityLevel: 'ERROR', totalConformanceErrorChecks: 4409, }, }; return Promise.resolve({ ok: true, status: 200, json: () => Promise.resolve(mockResponse), } as Response); } // Default case for unhandled URLs return Promise.reject(new Error(`Invalid URL or options in test: ${urlString}`)); }); global.fetch = fetchSpy as unknown as typeof global.fetch; }); afterEach(() => { jest.clearAllMocks(); }); afterAll(() => { // Restore original fetch global.fetch = originalFetch; }); function setup(url: string | undefined): void { render(<CcdaDisplay url={url} />, ({ children }) => ( <MedplumProvider medplum={medplum}>{children}</MedplumProvider> )); } test('Does not open Iframe when no URL passed in', async () => { setup(undefined); expect(screen.queryByTestId('ccda-iframe')).not.toBeInTheDocument(); expect(fetchSpy).not.toHaveBeenCalled(); }); test('Renders C-CDA', async () => { const sendCommandSpy = jest.spyOn(domUtils, 'sendCommand').mockImplementation(jest.fn(async () => {})); setup(EXAMPLE_CCDA_URL); expect(await screen.findByTestId('ccda-iframe')).toBeInTheDocument(); await act(async () => { fireEvent.load(screen.getByTitle('C-CDA Viewer')); }); expect(sendCommandSpy).toHaveBeenCalledWith(expect.any(HTMLIFrameElement), { command: 'loadCcdaXml', value: EXAMPLE_CCDA_URL, }); }); test('Validate button triggers validation process', async () => { setup(EXAMPLE_CCDA_URL); // Wait for the component to load expect(await screen.findByTestId('ccda-iframe')).toBeInTheDocument(); // Validate button should be present const validateButton = screen.getByRole('button', { name: /Validate/i }); expect(validateButton).toBeInTheDocument(); // Click the validate button await act(async () => { fireEvent.click(validateButton); }); // Should make fetch calls for both content and validation await waitFor(() => { // First call for CCDA content expect(fetchSpy).toHaveBeenCalledWith(EXAMPLE_CCDA_URL); // Second call for validation API const calls = fetchSpy.mock.calls; const validationCall = calls.find( (call) => call[0].toString().includes(VALIDATION_URL_PATTERN) && call[1]?.method === 'POST' ); expect(validationCall).toBeTruthy(); expect(validationCall[1].credentials).toBe('omit'); expect(validationCall[1].body).toBeInstanceOf(FormData); }); // Should display validation results with 9 errors (5 + 4) await waitFor(() => { expect(screen.getByText(/Validation Results:/)).toBeInTheDocument(); expect(screen.getByText(/9 errors found/)).toBeInTheDocument(); }); // Download button should be visible expect(screen.getByRole('button', { name: /Download Full Results/i })).toBeInTheDocument(); }); test('Download button exports validation results as JSON', async () => { setup(EXAMPLE_CCDA_URL); // Wait for the component to load expect(await screen.findByTestId('ccda-iframe')).toBeInTheDocument(); // Validate first const validateButton = screen.getByRole('button', { name: /Validate/i }); await act(async () => { fireEvent.click(validateButton); }); // Wait for validation results to appear await waitFor(() => { expect(screen.getByText(/Validation Results:/)).toBeInTheDocument(); }); // Find and click download button const downloadButton = screen.getByRole('button', { name: /Download Full Results/i }); await act(async () => { fireEvent.click(downloadButton); }); // Check that exportJsonFile was called with the validation results expect(exportJsonFileSpy).toHaveBeenCalledWith(expect.any(String), 'ccda-validation-results'); }); test('Validation with service error returns appropriate message', async () => { // Override fetch mock for this test to return a validation service error const originalFetchSpy = fetchSpy; fetchSpy = jest.fn().mockImplementation((url: string | URL, options?: RequestInit) => { const urlString = url.toString(); // Still handle content retrieval normally if (urlString === EXAMPLE_CCDA_URL && (!options?.method || options.method === 'GET')) { return originalFetchSpy(url, options); } // For validation API, return service error if (urlString.includes(VALIDATION_URL_PATTERN) && options?.method === 'POST') { return Promise.resolve({ ok: true, status: 200, json: () => Promise.resolve({ resultsMetaData: { serviceError: true, serviceErrorMessage: 'Failed to validate CCDA document', resultMetaData: [], // Empty array of results }, }), } as Response); } return Promise.reject(new Error(`Invalid URL or options in test: ${urlString}`)); }); global.fetch = fetchSpy as unknown as typeof global.fetch; setup(EXAMPLE_CCDA_URL); // Wait for the component to load expect(await screen.findByTestId('ccda-iframe')).toBeInTheDocument(); // Click validate const validateButton = screen.getByRole('button', { name: /Validate/i }); await act(async () => { fireEvent.click(validateButton); }); // Should still display validation results with 0 errors await waitFor(() => { expect(screen.getByText(/Validation Results:/)).toBeInTheDocument(); expect(screen.getByText(/0 errors found/)).toBeInTheDocument(); }); }); test('Handles API validation failure correctly', async () => { // Mock console.error to check if error is logged const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(() => {}); // Override fetch mock for this test to return a failed validation response const originalFetchSpy = fetchSpy; fetchSpy = jest.fn().mockImplementation((url: string | URL, options?: RequestInit) => { const urlString = url.toString(); // Still handle content retrieval normally if (urlString === EXAMPLE_CCDA_URL && (!options?.method || options.method === 'GET')) { return originalFetchSpy(url, options); } // For validation API, return a server error response if (urlString.includes(VALIDATION_URL_PATTERN) && options?.method === 'POST') { return Promise.resolve({ ok: false, status: 500, statusText: 'Internal Server Error', } as Response); } return Promise.reject(new Error(`Invalid URL or options in test: ${urlString}`)); }); global.fetch = fetchSpy as unknown as typeof global.fetch; setup(EXAMPLE_CCDA_URL); // Wait for the component to load expect(await screen.findByTestId('ccda-iframe')).toBeInTheDocument(); // Click validate const validateButton = screen.getByRole('button', { name: /Validate/i }); await act(async () => { fireEvent.click(validateButton); }); // Wait for validation process to complete await waitFor(() => { // Verify the validation button is no longer in "validating" state expect(screen.getByRole('button', { name: /Validate/i })).toBeEnabled(); }); // Should not display any validation results expect(screen.queryByText(/Validation Results:/)).not.toBeInTheDocument(); expect(screen.queryByRole('button', { name: /Download Full Results/i })).not.toBeInTheDocument(); // Should log the error expect(consoleErrorSpy).toHaveBeenCalledWith('CCDA validation error:', expect.any(Error)); // Clean up consoleErrorSpy.mockRestore(); }); });

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