Skip to main content
Glama
SignAddendum.test.tsx8.04 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 { Encounter, Provenance } from '@medplum/fhirtypes'; import { DrAliceSmith, MockClient } from '@medplum/mock'; import { MemoryRouter } from 'react-router'; import { describe, expect, test, beforeEach, vi } from 'vitest'; import { SignAddendum } from './SignAddendum'; import { ChartNoteStatus } from '../../types/encounter'; import * as notifications from '../../utils/notifications'; const mockEncounter: Encounter = { resourceType: 'Encounter', id: 'encounter-123', status: 'finished', class: { code: 'AMB', system: 'http://terminology.hl7.org/CodeSystem/v3-ActCode' }, subject: { reference: 'Patient/patient-123' }, }; const mockProvenance: Provenance = { resourceType: 'Provenance', id: 'provenance-123', target: [{ reference: 'Encounter/encounter-123' }], recorded: '2024-01-01T10:00:00Z', agent: [ { who: { reference: 'Practitioner/practitioner-123', display: 'Dr. Test' }, }, ], }; describe('SignAddendumCard', () => { let medplum: MockClient; beforeEach(async () => { medplum = new MockClient(); await medplum.createResource(DrAliceSmith); vi.clearAllMocks(); }); const setup = (props: Partial<Parameters<typeof SignAddendum>[0]> = {}): ReturnType<typeof render> => { return render( <MemoryRouter> <MedplumProvider medplum={medplum}> <MantineProvider> <SignAddendum encounter={mockEncounter} provenances={[]} chartNoteStatus={ChartNoteStatus.Unsigned} {...props} /> </MantineProvider> </MedplumProvider> </MemoryRouter> ); }; test('returns null when no provenances', async () => { await act(async () => { const { container } = setup({ provenances: [] }); await waitFor(() => { expect(container.firstChild).toBeNull(); }); }); }); test('renders provenance information', async () => { await act(async () => { setup({ provenances: [mockProvenance] }); }); await waitFor(() => { expect(screen.getByText(/Signed by/i)).toBeInTheDocument(); expect(screen.getByText(/Dr\. Test/i)).toBeInTheDocument(); }); }); test('displays signed and locked status for last provenance', async () => { await act(async () => { setup({ provenances: [mockProvenance], chartNoteStatus: ChartNoteStatus.SignedAndLocked, }); }); await waitFor(() => { expect(screen.getByText(/Signed and Locked by/i)).toBeInTheDocument(); }); }); test('displays addendum form', async () => { await act(async () => { setup({ provenances: [mockProvenance] }); }); await waitFor(() => { expect(screen.getByPlaceholderText('Add an addendum to this Visit...')).toBeInTheDocument(); expect(screen.getByRole('button', { name: 'Add Addendum' })).toBeInTheDocument(); }); }); test('creates addendum when form is submitted', async () => { const user = userEvent.setup(); const createResourceSpy = vi.spyOn(medplum, 'createResource').mockResolvedValue({ resourceType: 'DocumentReference', id: 'doc-new', status: 'current', date: new Date().toISOString(), author: [{ display: 'Dr. Alice Smith' }], } as any); await act(async () => { setup({ provenances: [mockProvenance] }); }); await waitFor(() => { expect(screen.getByPlaceholderText('Add an addendum to this Visit...')).toBeInTheDocument(); }); const textarea = screen.getByPlaceholderText('Add an addendum to this Visit...'); await user.type(textarea, 'Test addendum text'); const submitButton = screen.getByRole('button', { name: 'Add Addendum' }); await user.click(submitButton); await waitFor(() => { expect(createResourceSpy).toHaveBeenCalled(); }); }); test('disables submit button when textarea is empty', async () => { await act(async () => { setup({ provenances: [mockProvenance] }); }); await waitFor(() => { const submitButton = screen.getByRole('button', { name: 'Add Addendum' }); expect(submitButton).toBeInTheDocument(); }); const submitButton = screen.getByRole('button', { name: 'Add Addendum' }); expect(submitButton).toBeDisabled(); }); test('disables submit button when only whitespace', async () => { const user = userEvent.setup(); await act(async () => { setup({ provenances: [mockProvenance] }); }); await waitFor(() => { expect(screen.getByPlaceholderText('Add an addendum to this Visit...')).toBeInTheDocument(); }); const textarea = screen.getByPlaceholderText('Add an addendum to this Visit...'); await user.type(textarea, ' '); await waitFor(() => { const submitButton = screen.getByRole('button', { name: 'Add Addendum' }); expect(submitButton).toBeDisabled(); }); }); test('loads and displays addendums from DocumentReference', async () => { const mockDocumentReference = { resourceType: 'DocumentReference' as const, id: 'doc-123', status: 'current' as const, date: '2024-01-02T10:00:00Z', author: [{ display: 'Dr. Author' }], content: [ { attachment: { contentType: 'text/plain', data: btoa('Test addendum content'), }, }, ], }; vi.spyOn(medplum, 'searchResources').mockResolvedValue([mockDocumentReference] as any); await act(async () => { setup({ provenances: [mockProvenance] }); }); await waitFor(() => { expect(screen.getByText(/Addendum by/i)).toBeInTheDocument(); expect(screen.getByText('Test addendum content')).toBeInTheDocument(); }); }); test('handles errors when loading addendums', async () => { const error = new Error('Failed to load addendums'); vi.spyOn(medplum, 'searchResources').mockRejectedValue(error); const errorNotificationSpy = vi.spyOn(notifications, 'showErrorNotification'); await act(async () => { setup({ provenances: [mockProvenance] }); }); await waitFor(() => { expect(errorNotificationSpy).toHaveBeenCalledWith(error); }); }); test('handles errors when creating addendum', async () => { const user = userEvent.setup(); const error = new Error('Failed to create addendum'); vi.spyOn(medplum, 'createResource').mockRejectedValue(error); const errorNotificationSpy = vi.spyOn(notifications, 'showErrorNotification'); await act(async () => { setup({ provenances: [mockProvenance] }); }); await waitFor(() => { expect(screen.getByPlaceholderText('Add an addendum to this Visit...')).toBeInTheDocument(); }); const textarea = screen.getByPlaceholderText('Add an addendum to this Visit...'); await user.type(textarea, 'Test addendum'); const submitButton = screen.getByRole('button', { name: 'Add Addendum' }); await user.click(submitButton); await waitFor(() => { expect(errorNotificationSpy).toHaveBeenCalledWith(error); }); }); test('displays multiple provenances', async () => { const provenance2: Provenance = { ...mockProvenance, id: 'provenance-456', recorded: '2024-01-02T10:00:00Z', agent: [ { who: { reference: 'Practitioner/practitioner-456', display: 'Dr. Another' }, }, ], }; await act(async () => { setup({ provenances: [mockProvenance, provenance2] }); }); await waitFor(() => { expect(screen.getByText(/Dr\. Test/i)).toBeInTheDocument(); expect(screen.getByText(/Dr\. Another/i)).toBeInTheDocument(); }); }); });

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