import { render, screen, fireEvent } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { withTestQueryProvider } from '@/react/test-utils/withTestQuery'; import { withUserProvider } from '@/react/test-utils/withUserProvider'; import { withTestRouter } from '@/react/test-utils/withRouter'; import { UserViewModel } from '@/portainer/models/user'; import { HelmTemplatesList } from './HelmTemplatesList'; import { Chart } from './HelmTemplatesListItem'; // Sample test data const mockCharts: Chart[] = [ { name: 'test-chart-1', description: 'Test Chart 1 Description', annotations: { category: 'database', }, }, { name: 'test-chart-2', description: 'Test Chart 2 Description', annotations: { category: 'database', }, }, { name: 'nginx-chart', description: 'Nginx Web Server', annotations: { category: 'web', }, }, ]; const selectActionMock = vi.fn(); function renderComponent({ loading = false, titleText = 'Test Helm Templates', charts = mockCharts, selectAction = selectActionMock, } = {}) { const user = new UserViewModel({ Username: 'user' }); const Wrapped = withTestQueryProvider( withUserProvider( withTestRouter(() => ( )), user ) ); return { ...render(), user }; } describe('HelmTemplatesList', () => { beforeEach(() => { selectActionMock.mockClear(); }); it('should display title and charts list', async () => { renderComponent(); // Check for the title expect(screen.getByText('Test Helm Templates')).toBeInTheDocument(); // Check for charts expect(screen.getByText('test-chart-1')).toBeInTheDocument(); expect(screen.getByText('Test Chart 1 Description')).toBeInTheDocument(); expect(screen.getByText('nginx-chart')).toBeInTheDocument(); expect(screen.getByText('Nginx Web Server')).toBeInTheDocument(); }); it('should call selectAction when a chart is clicked', async () => { renderComponent(); // Find the first chart item const firstChartItem = screen.getByText('test-chart-1').closest('button'); expect(firstChartItem).not.toBeNull(); // Click on the chart item if (firstChartItem) { fireEvent.click(firstChartItem); } // Check if selectAction was called with the correct chart expect(selectActionMock).toHaveBeenCalledWith(mockCharts[0]); }); it('should filter charts by text search', async () => { renderComponent(); const user = userEvent.setup(); // Find search input and type "nginx" const searchInput = screen.getByPlaceholderText('Search...'); await user.type(searchInput, 'nginx'); // Wait 300ms for debounce await new Promise((resolve) => { setTimeout(() => { resolve(undefined); }, 300); }); // Should show only nginx chart expect(screen.getByText('nginx-chart')).toBeInTheDocument(); expect(screen.queryByText('test-chart-1')).not.toBeInTheDocument(); expect(screen.queryByText('test-chart-2')).not.toBeInTheDocument(); }); it('should filter charts by category', async () => { renderComponent(); const user = userEvent.setup(); // Find the category select const categorySelect = screen.getByText('Select a category'); await user.click(categorySelect); // Select "web" category const webCategory = screen.getByText('web', { selector: '[tabindex="-1"]', }); await user.click(webCategory); // Should show only web category charts expect(screen.queryByText('nginx-chart')).toBeInTheDocument(); expect(screen.queryByText('test-chart-1')).not.toBeInTheDocument(); expect(screen.queryByText('test-chart-2')).not.toBeInTheDocument(); }); it('should show loading message when loading prop is true', async () => { renderComponent({ loading: true }); // Check for loading message expect(screen.getByText('Loading...')).toBeInTheDocument(); expect( screen.getByText('Initial download of Helm charts can take a few minutes') ).toBeInTheDocument(); }); it('should show empty message when no charts are available', async () => { renderComponent({ charts: [] }); // Check for empty message expect(screen.getByText('No helm charts available.')).toBeInTheDocument(); }); it('should show no results message when search has no matches', async () => { renderComponent(); const user = userEvent.setup(); // Find search input and type text that won't match any charts const searchInput = screen.getByPlaceholderText('Search...'); await user.type(searchInput, 'nonexistent chart'); // Wait 300ms for debounce await new Promise((resolve) => { setTimeout(() => { resolve(undefined); }, 300); }); // Check for no results message expect(screen.getByText('No Helm charts found')).toBeInTheDocument(); }); it('should handle keyboard navigation and selection', async () => { renderComponent(); const user = userEvent.setup(); // Find the first chart item const firstChartItem = screen.getByText('test-chart-1').closest('button'); expect(firstChartItem).not.toBeNull(); // Focus and press Enter if (firstChartItem) { (firstChartItem as HTMLElement).focus(); await user.keyboard('{Enter}'); } // Check if selectAction was called with the correct chart expect(selectActionMock).toHaveBeenCalledWith(mockCharts[0]); }); });