import React from 'react'; import { vi, describe, it, expect, beforeEach } from 'vitest'; import userEvent from '@testing-library/user-event'; import { render, screen, waitFor } from '@testing-library/react'; import { type StockItemDTO } from '../core/api/types/stockItem/StockItem'; import { handleMutate } from '../utils'; import { launchAddOrEditStockItemWorkspace } from './stock-item.utils'; import { useStockItemsPages } from './stock-items-table.resource'; import StockItemsTableComponent from './stock-items-table.component'; const mockUseStockItemsPages = vi.mocked(useStockItemsPages); vi.mock('./stock-items-table.resource', () => ({ useStockItemsPages: vi.fn(), })); vi.mock('../utils', () => ({ handleMutate: vi.fn(), })); vi.mock('./stock-item.utils', () => ({ launchAddOrEditStockItemWorkspace: vi.fn(), })); describe('StockItemsTableComponent', () => { beforeEach(() => { mockUseStockItemsPages.mockReturnValue({ isLoading: false, items: Array(25) .fill(null) .map((_, index) => ({ uuid: `item-${index}`, commonName: `Test Item ${index}`, drugUuid: index % 2 === 0 ? `drug-${index}` : null, conceptName: `Concept ${index}`, dispensingUnitName: `Unit ${index}`, defaultStockOperationsUoMName: `UoM ${index}`, reorderLevel: index * 10, reorderLevelUoMName: 'Units', })) as StockItemDTO[], pagination: { results: [], totalPages: 3, currentPage: 1, paginated: true, showNextButton: true, showPreviousButton: false, goTo: vi.fn(), goToNext: vi.fn(), goToPrevious: vi.fn(), }, error: null, totalCount: 25, currentPageSize: 10, setPageSize: vi.fn(), pageSizes: [10, 20, 30], currentPage: 1, setCurrentPage: vi.fn(), isDrug: '', setDrug: vi.fn(), setSearchString: vi.fn(), }); }); it('renders initial state and UI elements correctly', async () => { const user = userEvent.setup(); render(); expect(screen.getByRole('radio', { name: /all/i })).toBeInTheDocument(); expect(screen.getByRole('radio', { name: /^pharmaceuticals$/i })).toBeInTheDocument(); expect(screen.getByRole('radio', { name: /non pharmaceuticals/i })).toBeInTheDocument(); expect(screen.getByRole('button', { name: /import/i })).toBeInTheDocument(); expect(screen.getByRole('button', { name: /settings/i })).toBeInTheDocument(); expect(screen.getByRole('searchbox', { name: /filter table/i })).toBeInTheDocument(); const settingsMenuButton = screen.getByRole('button', { name: /settings/i }); await user.click(settingsMenuButton); await screen.findByText(/refresh/i); expect(screen.getByRole('button', { name: /generic name/i })).toBeInTheDocument(); expect(screen.getByRole('button', { name: /common name/i })).toBeInTheDocument(); expect(screen.getByRole('button', { name: /dispensing uom/i })).toBeInTheDocument(); expect(screen.getByRole('button', { name: /bulk packaging/i })).toBeInTheDocument(); expect(screen.getByRole('button', { name: /reorder level/i })).toBeInTheDocument(); }); it('displays skeleton loader when isLoading is true', () => { mockUseStockItemsPages.mockReturnValue({ ...mockUseStockItemsPages, isLoading: true, items: [], pagination: undefined, totalCount: 0, currentPageSize: 0, currentPage: 0, setCurrentPage: function (value: React.SetStateAction): void { throw new Error('Function not implemented.'); }, setPageSize: function (value: React.SetStateAction): void { throw new Error('Function not implemented.'); }, pageSizes: [], error: undefined, isDrug: '', setDrug: undefined, setSearchString: function (value: any): void { throw new Error('Function not implemented.'); }, }); render(); expect(screen.getByRole('progressbar')).toBeInTheDocument(); }); it('renders table rows correctly based on items prop', () => { render(); expect(screen.getByRole('cell', { name: /test item 0/i })).toBeInTheDocument(); expect(screen.getAllByRole('cell', { name: /drug/i }).length).toBeGreaterThan(0); expect(screen.getAllByRole('cell', { name: /other/i }).length).toBeGreaterThan(0); }); it('updates search input and triggers search function', async () => { const user = userEvent.setup(); render(); const searchInput = screen.getByRole('searchbox'); await user.type(searchInput, 'test search'); await waitFor( () => { expect(mockUseStockItemsPages().setSearchString).toHaveBeenCalledWith('test search'); }, { timeout: 2000 }, ); }); it('updates pagination when page or page size changes', async () => { const user = userEvent.setup(); render(); const nextPageButton = screen.getByLabelText(/next page/i); await user.click(nextPageButton); expect(mockUseStockItemsPages().setCurrentPage).toHaveBeenCalled(); const pageSizeSelect = screen.getByLabelText(/items per page/i); await user.selectOptions(pageSizeSelect, '20'); expect(mockUseStockItemsPages().setPageSize).toHaveBeenCalledWith(20); }); it('triggers handleRefresh when refresh button is clicked', async () => { const user = userEvent.setup(); render(); const menuButton = screen.getByTestId('stock-items-menu'); expect(menuButton).toBeInTheDocument(); await user.click(menuButton); const refreshButton = await screen.findByText(/refresh/i); expect(refreshButton).toBeInTheDocument(); await user.click(refreshButton); await waitFor(() => { expect(handleMutate).toHaveBeenCalledWith(expect.stringContaining('/stockmanagement/stockitem')); }); }); it('triggers launchAddOrEditStockItemWorkspace when edit button is clicked', async () => { const user = userEvent.setup(); render(); const editButtons = screen.getAllByLabelText(/edit stock item/i); await user.click(editButtons[0]); expect(launchAddOrEditStockItemWorkspace).toHaveBeenCalledWith( expect.any(Function), expect.objectContaining({ uuid: 'item-0' }), ); }); });