portainer/app/react/kubernetes/helm/HelmTemplates/HelmTemplatesList.test.tsx

219 lines
6.4 KiB
TypeScript

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 { Chart } from '../types';
import { HelmTemplatesList } from './HelmTemplatesList';
// Sample test data
const mockCharts: Chart[] = [
{
name: 'test-chart-1',
description: 'Test Chart 1 Description',
repo: 'https://example.com',
annotations: {
category: 'database',
},
version: '1.0.0',
versions: ['1.0.0', '1.0.1'],
},
{
name: 'test-chart-2',
description: 'Test Chart 2 Description',
repo: 'https://example.com',
annotations: {
category: 'database',
},
version: '1.0.0',
versions: ['1.0.0', '1.0.1'],
},
{
name: 'nginx-chart',
description: 'Nginx Web Server',
repo: 'https://example.com/2',
annotations: {
category: 'web',
},
version: '1.0.0',
versions: ['1.0.0', '1.0.1'],
},
];
const selectActionMock = vi.fn();
function renderComponent({
loading = false,
charts = mockCharts,
selectAction = selectActionMock,
selectedRegistry = '',
} = {}) {
const user = new UserViewModel({ Username: 'user' });
const registries = ['https://example.com', 'https://example.com/2'];
const Wrapped = withTestQueryProvider(
withUserProvider(
withTestRouter(() => (
<HelmTemplatesList
isLoading={loading}
charts={charts}
selectAction={selectAction}
registries={registries}
selectedRegistry={selectedRegistry}
setSelectedRegistry={() => {}}
/>
)),
user
)
);
return { ...render(<Wrapped />), user };
}
describe('HelmTemplatesList', () => {
beforeEach(() => {
selectActionMock.mockClear();
});
it('should display title and charts list', async () => {
renderComponent();
// Check for the title
expect(screen.getByText('Helm chart')).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();
expect(screen.getByText('https://example.com/2')).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, charts: [] });
// Check for loading message
expect(screen.getByText('Loading helm charts...')).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 and a registry is selected', async () => {
renderComponent({ charts: [], selectedRegistry: 'https://example.com' });
// Check for empty message
expect(
screen.getByText('No helm charts available in this registry.')
).toBeInTheDocument();
});
it("should show 'select registry' message when no charts are available and no registry is selected", async () => {
renderComponent({ charts: [] });
// Check for message
expect(
screen.getByText(
'Please select a registry to view available Helm charts.'
)
).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]);
});
});