import React from 'react'; import { vi, describe, it, expect, beforeEach } from 'vitest'; import userEvent from '@testing-library/user-event'; import { render, screen } from '@testing-library/react'; import { useFormContext, type UseFormReturn } from 'react-hook-form'; import { useConfig, useSession } from '@openmrs/esm-framework'; import { formatForDatePicker } from '../../constants'; import { receiptOperationTypeMock } from '@mocks'; import { type BaseStockOperationItemFormData } from '../validation-schema'; import { type StockItemDTO } from '../../core/api/types/stockItem/StockItem'; import { useFilterableStockItems } from './hooks/useFilterableStockItems'; import { useStockItem } from '../../stock-items/stock-items.resource'; import { useStockOperations } from '../stock-operations.resource'; import { useStockOperationTypes } from '../../stock-lookups/stock-lookups.resource'; import useParties from './hooks/useParties'; import StockOperationForm from './stock-operation-form.component'; const mockUseConfig = vi.mocked(useConfig); const mockUseFilterableStockItems = vi.mocked(useFilterableStockItems); const mockUseFormContext = vi.mocked(useFormContext); const mockUseParties = vi.mocked(useParties); const mockUseSession = vi.mocked(useSession); const mockUseStockItem = vi.mocked(useStockItem); const mockUseStockOperations = vi.mocked(useStockOperations); const mockUseStockOperationTypes = vi.mocked(useStockOperationTypes); vi.mock('../../stock-lookups/stock-lookups.resource', () => ({ useStockOperationTypes: vi.fn(), useUsers: vi.fn().mockReturnValue({ items: { results: [] }, isLoading: false }), useUser: vi.fn().mockReturnValue({ data: { display: 'Test User' }, isLoading: false, error: null }), })); vi.mock('../stock-operations.resource', () => ({ operationStatusColor: vi.fn(() => 'some-color'), getStockOperationLinks: vi.fn(), useStockOperations: vi.fn().mockReturnValue({ items: { results: [], links: [], totalCount: 0, }, isLoading: false, error: null, }), useStockOperationAndItems: vi.fn().mockReturnValue({ items: { results: [] }, isLoading: false, error: null, }), })); vi.mock('../../stock-items/stock-items.resource', () => ({ useStockItem: vi.fn().mockReturnValue({ isLoading: false, item: {}, error: null, }), useStockItems: vi.fn().mockReturnValue({ isLoading: false, error: null, items: {}, }), useStockBatches: vi.fn().mockReturnValue({ isLoading: false, error: null, items: {}, }), })); vi.mock('./hooks/useFilterableStockItems', () => ({ useFilterableStockItems: vi.fn().mockReturnValue({ stockItemsList: [], setLimit: vi.fn(), setRepresentation: vi.fn(), setSearchString: vi.fn(), isLoading: false, }), })); vi.mock('./hooks/useParties', () => ({ default: vi.fn() })); vi.mock('react-hook-form', () => ({ useForm: vi.fn().mockReturnValue({ watch: vi.fn(), formState: { errors: {}, }, resetField: vi.fn(), getValues: vi.fn(), setValue: vi.fn(), handleSubmit: vi.fn(), trigger: vi.fn().mockReturnValue(true), }), useFormContext: vi.fn().mockReturnValue({ watch: vi.fn(), formState: { errors: {}, isDirty: false, isLoading: false, isSubmitted: false, isSubmitSuccessful: false, isSubmitting: false, isValid: true, isValidating: false, submitCount: 0, touchedFields: {}, dirtyFields: {}, disabled: false, }, resetField: vi.fn(), getValues: vi.fn(), setValue: vi.fn(), handleSubmit: vi.fn(), trigger: vi.fn().mockReturnValue(true), }), Controller: ({ render }) => render({ field: {}, fieldState: {} }), FormProvider: ({ children }: { children: React.ReactNode }) => <>{children}, })); vi.mock('../../stock-items/add-stock-item/batch-information/batch-information.resource', () => ({ useStockItemBatchInformationHook: vi.fn().mockReturnValue({ items: [], totalCount: 0, currentPage: 1, currentPageSize: 10, setCurrentPage: vi.fn(), setPageSize: vi.fn(), pageSizes: [], isLoading: false, error: undefined, setSearchString: vi.fn(), setStockItemUuid: vi.fn(), setLocationUuid: vi.fn(), setPartyUuid: vi.fn(), setStockBatchUuid: vi.fn(), }), })); describe('Stock Operation step 2 (stock operation items details)', () => { beforeEach(() => { const mockStockOperationTypes = { types: { results: [], links: [], totalCount: 0, }, isLoading: false, error: null, }; mockUseStockOperationTypes.mockReturnValue(mockStockOperationTypes); mockUseStockOperations.mockReturnValue({ items: { results: [], links: [], totalCount: 0, }, isLoading: false, error: null, }); mockUseConfig.mockReturnValue({ autoPopulateResponsiblePerson: true }); mockUseParties.mockReturnValue({ destinationParties: [], destinationPartiesFilter: vi.fn(), destinationTags: [], error: undefined, isLoading: false, mutate: vi.fn(), parties: [], sourceParties: [], sourcePartiesFilter: vi.fn(), sourceTags: [], }); mockUseSession.mockReturnValue({ authenticated: true, sessionId: 'test-session-id', user: { uuid: 'test-user-uuid', display: 'Test User', username: 'testuser', systemId: 'test-system-id', userProperties: {}, person: { uuid: 'test-person-uuid' }, privileges: [], roles: [], retired: false, links: [], locale: 'en', allowedLocales: ['en'], }, sessionLocation: { uuid: 'test-location-uuid', display: 'Test Location', links: [], }, }); }); it('should have both previous and next btns', async () => { const user = userEvent.setup(); render( , ); // MOVE TO STEP 2 await user.click(screen.getByRole('button', { name: /Next/i })); expect(screen.getByRole('button', { name: /Next/i })).toBeInTheDocument(); expect(screen.getByTestId('previous-btn')).toBeInTheDocument(); }); it('should render stock operation items table with item search component', async () => { const user = userEvent.setup(); render( , ); const nextButton = screen.getByRole('button', { name: /Next/i }); expect(nextButton).toBeInTheDocument(); await user.click(nextButton); expect(screen.getByRole('table')).toBeInTheDocument(); expect( screen.getByRole('searchbox', { name(accessibleName, element) { return element.getAttribute('id') === 'search-stock-operation-item'; }, }), ).toBeInTheDocument(); }); it('should search stock operation item and render results', async () => { const user = userEvent.setup(); const mocksetSearchString = vi.fn(); mockUseFilterableStockItems.mockReturnValue({ stockItemsList: [ { uuid: 'mock-uuid', commonName: 'mock-common-name', drugName: 'mock-common-name' }, ] as Array, setLimit: vi.fn(), setRepresentation: vi.fn(), isLoading: false, setSearchString: mocksetSearchString, }); render( , ); // ----- CLICK NEXT TO MOVE TO STEP 2 --------- await user.click(screen.getByRole('button', { name: /Next/i })); // ------------------------------- const searchInput = screen.getByRole('searchbox', { name: /search/i, }); expect(searchInput).toBeInTheDocument(); await user.click(searchInput); await user.type(searchInput, 'stock'); expect(mocksetSearchString).toHaveBeenCalledWith('stock'); expect(screen.getByText('mock-common-name')); }); it('should properly handle stock operation item selection', async () => { const user = userEvent.setup(); mockUseFilterableStockItems.mockReturnValue({ stockItemsList: [ { uuid: 'mock-uuid', commonName: 'mock-common-name', drugName: 'mock-common-name' }, ] as Array, setLimit: vi.fn(), setRepresentation: vi.fn(), isLoading: false, setSearchString: vi.fn(), }); render( , ); // ----- CLICK NEXT TO MOVE TO STEP 2 --------- await user.click(screen.getByRole('button', { name: /Next/i })); // ------------------------------- const searchInput = screen.getByRole('searchbox', { name: (_, element) => element.getAttribute('id') === 'search-stock-operation-item', }); await user.click(searchInput); await user.type(searchInput, 'stock'); await user.click(screen.getByText('mock-common-name')); // Look for common name at the top of workspace expect(screen.getByText(/no drug name available|no common name available|mock-common-name/i)).toBeInTheDocument(); }); it('should render stock operation items in data table', async () => { const user = userEvent.setup(); mockUseStockItem.mockReturnValue({ isLoading: false, error: null, item: { commonName: 'mock-stock-item-common name', uuid: 'mock-uuid' } as StockItemDTO, }); const mockQuantity = 99999; const mockExpiration = new Date(); mockUseFormContext.mockReturnValue({ watch: vi.fn().mockReturnValue([ { quantity: mockQuantity, expiration: mockExpiration, }, ] as BaseStockOperationItemFormData), resetField: vi.fn(), formState: { errors: {}, isDirty: false, isLoading: false, isSubmitted: false, isSubmitSuccessful: false, isSubmitting: false, isValid: true, isValidating: false, submitCount: 0, touchedFields: {}, dirtyFields: {}, disabled: false, }, getValues: vi.fn(), setValue: vi.fn(), handleSubmit: vi.fn(), trigger: vi.fn().mockReturnValue(true), } as unknown as UseFormReturn); render( , ); // ----- CLICK NEXT TO MOVE TO STEP 2 --------- await user.click(screen.getByRole('button', { name: /Next/i })); // ------------------------------- expect(screen.getByText(mockQuantity.toLocaleString())).toBeInTheDocument(); //Find by quentity expect(screen.getByText(formatForDatePicker(mockExpiration))).toBeInTheDocument(); //Find by quentity }); });