import React from 'react'; import userEvent from '@testing-library/user-event'; import { render, screen } from '@testing-library/react'; import { useConceptId } from '@hooks/useConceptId'; import { useConceptLookup } from '@hooks/useConceptLookup'; import ConceptSearch from './concept-search.component'; import type { FormField } from '@openmrs/esm-form-engine-lib'; import type { Concept } from '@types'; const formField: FormField = { id: '1', type: 'obs', questionOptions: { rendering: 'text', }, }; const concepts: Array = [ { uuid: '123', display: 'Concept 1', datatype: { uuid: '456', name: 'Coded' }, mappings: [{ display: 'CIEL:1606', conceptMapType: { display: 'SAME-AS' } }], answers: [ { uuid: '1', display: 'Answer 1' }, { uuid: '2', display: 'Answer 2' }, ], }, { uuid: '456', display: 'Concept 2', datatype: { uuid: '456', name: 'Date' }, mappings: [{ display: 'CIEL:1656', conceptMapType: { display: 'SAME-AS' } }], }, ]; const mockUseConceptLookup = jest.mocked(useConceptLookup); jest.mock('@hooks/useConceptLookup', () => ({ ...jest.requireActual('@hooks/useConceptLookup'), useConceptLookup: jest.fn(), })); const mockUseConceptId = jest.mocked(useConceptId); jest.mock('@hooks/useConceptId', () => ({ ...jest.requireActual('@hooks/useConceptId'), useConceptId: jest.fn(), })); const onSelectConcept = jest.fn(); const mockSetFormField = jest.fn(); const setConcept = jest.fn(); const setIsConceptValid = jest.fn(); jest.mock('../../../form-field-context', () => ({ ...jest.requireActual('../../../form-field-context'), useFormField: () => ({ formField, setFormField: mockSetFormField, setConcept, setIsConceptValid }), })); describe('Concept search component', () => { beforeEach(() => { mockUseConceptLookup.mockReturnValue({ concepts: concepts, conceptLookupError: null, isLoadingConcepts: false }); mockUseConceptId.mockReturnValue({ concept: null, conceptName: null, conceptNameLookupError: null, isLoadingConcept: false, }); }); it('renders', () => { renderComponent(); expect(screen.getByRole('searchbox', { name: /search for a backing concept/i })).toBeInTheDocument(); }); it('allows user to search and select a concept', async () => { mockUseConceptLookup.mockReturnValue({ concepts: concepts, conceptLookupError: null, isLoadingConcepts: false }); mockUseConceptId.mockReturnValue({ concept: null, conceptName: null, conceptNameLookupError: null, isLoadingConcept: false, }); const user = userEvent.setup(); renderComponent(); const searchInput = screen.getByRole('searchbox', { name: /search for a backing concept/i }); await user.click(searchInput); await user.type(searchInput, 'Concept 1'); const conceptMenuItem = await screen.findByRole('menuitem', { name: /concept 1/i, }); expect(conceptMenuItem).toBeInTheDocument(); expect(searchInput).toHaveDisplayValue(/concept 1/i); }); it('shows loading spinner when concept is loading', async () => { mockUseConceptLookup.mockReturnValue({ concepts: concepts, conceptLookupError: null, isLoadingConcepts: true }); mockUseConceptId.mockReturnValue({ concept: null, conceptName: null, conceptNameLookupError: null, isLoadingConcept: false, }); const user = userEvent.setup(); renderComponent(); const searchInput = screen.getByRole('searchbox', { name: /search for a backing concept/i }); await user.click(searchInput); await user.type(searchInput, 'Does not exist'); expect(screen.getByText(/searching\.\.\./i)).toBeInTheDocument(); }); it('displays an error message if the searched concept cannot be found', async () => { mockUseConceptLookup.mockReturnValue({ concepts: [], conceptLookupError: Error(), isLoadingConcepts: false }); mockUseConceptId.mockReturnValue({ concept: null, conceptName: null, conceptNameLookupError: null, isLoadingConcept: false, }); renderComponent(); expect(screen.getByText(/error fetching concepts/i)).toBeInTheDocument(); expect(screen.getByText(/please try again\./i)).toBeInTheDocument(); }); it('displays an error with a link to ocl when concept query gives empty results', async () => { mockUseConceptLookup.mockReturnValue({ concepts: [], conceptLookupError: null, isLoadingConcepts: false }); mockUseConceptId.mockReturnValue({ concept: null, conceptName: null, conceptNameLookupError: null, isLoadingConcept: false, }); const user = userEvent.setup(); renderComponent(); const searchInput = screen.getByRole('searchbox', { name: /search for a backing concept/i }); await user.click(searchInput); await user.type(searchInput, 'Does not exist'); expect(screen.getByText(/no concepts were found that match/i)).toBeInTheDocument(); expect(screen.getByText(/can't find a concept\?/i)).toBeInTheDocument(); expect( screen.getByRole('link', { name: /search in ocl/i, }), ).toBeInTheDocument(); }); }); function renderComponent() { render(); }