- Added IDs to input fields in CrowdSecConfig for better accessibility. - Updated labels to use <label> elements for checkboxes and inputs. - Improved error handling and user feedback in the CrowdSecConfig tests. - Enhanced test coverage for console enrollment and banned IP functionalities. fix: Update SecurityHeaders to include aria-label for delete button - Added aria-label to the delete button for better screen reader support. test: Add comprehensive tests for proxyHostsHelpers and validation utilities - Implemented tests for formatting and help text functions in proxyHostsHelpers. - Added validation tests for email and IP address formats. chore: Update vitest configuration for dynamic coverage thresholds - Adjusted coverage thresholds to be dynamic based on environment variables. - Included additional coverage reporters. chore: Update frontend-test-coverage script to reflect new coverage threshold - Increased minimum coverage requirement from 85% to 87.5%. fix: Ensure tests pass with consistent data in passwd file - Updated tests/etc/passwd to ensure consistent content.
228 lines
7.7 KiB
TypeScript
228 lines
7.7 KiB
TypeScript
import { render, screen, waitFor } from '@testing-library/react';
|
|
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
import DNSProviderForm from '../DNSProviderForm';
|
|
import userEvent from '@testing-library/user-event';
|
|
|
|
// Mock the hooks
|
|
const mockCreateMutation = {
|
|
mutateAsync: vi.fn(),
|
|
isPending: false,
|
|
};
|
|
const mockUpdateMutation = {
|
|
mutateAsync: vi.fn(),
|
|
isPending: false,
|
|
};
|
|
const mockTestCredentialsMutation = {
|
|
mutateAsync: vi.fn(),
|
|
isPending: false,
|
|
};
|
|
const mockEnableMultiCredentialsMutation = {
|
|
mutateAsync: vi.fn(),
|
|
isPending: false,
|
|
};
|
|
|
|
vi.mock('../../hooks/useDNSProviders', () => ({
|
|
useDNSProviderTypes: vi.fn(() => ({
|
|
data: [
|
|
{
|
|
type: 'cloudflare',
|
|
name: 'Cloudflare',
|
|
fields: [
|
|
{ name: 'api_token', label: 'API Token', type: 'password', required: true }
|
|
]
|
|
},
|
|
{
|
|
type: 'route53',
|
|
name: 'Route53',
|
|
fields: [
|
|
{ name: 'access_key_id', label: 'Access Key ID', type: 'text', required: true },
|
|
{ name: 'secret_access_key', label: 'Secret Access Key', type: 'password', required: true }
|
|
]
|
|
}
|
|
],
|
|
isLoading: false,
|
|
})),
|
|
useDNSProviderMutations: vi.fn(() => ({
|
|
createMutation: mockCreateMutation,
|
|
updateMutation: mockUpdateMutation,
|
|
testCredentialsMutation: mockTestCredentialsMutation,
|
|
})),
|
|
}));
|
|
|
|
vi.mock('../../hooks/useCredentials', () => ({
|
|
useEnableMultiCredentials: vi.fn(() => mockEnableMultiCredentialsMutation),
|
|
useCredentials: vi.fn(() => ({
|
|
data: [],
|
|
})),
|
|
}));
|
|
|
|
// Mock CredentialManager component to avoid complex nested testing
|
|
vi.mock('../CredentialManager', () => ({
|
|
default: () => <div data-testid="credential-manager">Credential Manager Mock</div>,
|
|
}));
|
|
|
|
// Mock translations
|
|
vi.mock('react-i18next', () => ({
|
|
useTranslation: () => ({
|
|
t: (key: string) => {
|
|
const translations: Record<string, string> = {
|
|
'dnsProviders.addProvider': 'Add DNS Provider',
|
|
'dnsProviders.editProvider': 'Edit DNS Provider',
|
|
'dnsProviders.providerName': 'Provider Name',
|
|
'dnsProviders.providerType': 'Provider Type',
|
|
'dnsProviders.propagationTimeout': 'Propagation Timeout (seconds)',
|
|
'dnsProviders.pollingInterval': 'Polling Interval (seconds)',
|
|
'dnsProviders.setAsDefault': 'Set as default provider',
|
|
'dnsProviders.advancedSettings': 'Advanced Settings',
|
|
'dnsProviders.testConnection': 'Test Connection',
|
|
'dnsProviders.testSuccess': 'Connection test successful',
|
|
'dnsProviders.testFailed': 'Connection test failed',
|
|
'common.create': 'Create',
|
|
'common.update': 'Update',
|
|
'common.cancel': 'Cancel',
|
|
};
|
|
return translations[key] || key;
|
|
},
|
|
}),
|
|
}));
|
|
|
|
describe('DNSProviderForm', () => {
|
|
const defaultProps = {
|
|
open: true,
|
|
onOpenChange: vi.fn(),
|
|
onSuccess: vi.fn(),
|
|
};
|
|
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
});
|
|
|
|
it('renders correctly in add mode', () => {
|
|
render(<DNSProviderForm {...defaultProps} />);
|
|
|
|
expect(screen.getByText('Add DNS Provider')).toBeInTheDocument();
|
|
expect(screen.getByLabelText('Provider Name')).toBeInTheDocument();
|
|
// Use role to find the trigger specifically
|
|
expect(screen.getByRole('combobox', { name: 'Provider Type' })).toBeInTheDocument();
|
|
});
|
|
|
|
it('populates fields when editing', async () => {
|
|
const provider = {
|
|
id: 1,
|
|
uuid: 'prov-uuid',
|
|
name: 'My Cloudflare',
|
|
provider_type: 'cloudflare' as const,
|
|
is_default: true,
|
|
enabled: true,
|
|
propagation_timeout: 180,
|
|
polling_interval: 10,
|
|
has_credentials: true,
|
|
success_count: 0,
|
|
failure_count: 0,
|
|
created_at: '2023-01-01',
|
|
updated_at: '2023-01-01',
|
|
};
|
|
|
|
render(<DNSProviderForm {...defaultProps} provider={provider} />);
|
|
|
|
expect(screen.getByText('Edit DNS Provider')).toBeInTheDocument();
|
|
expect(screen.getByDisplayValue('My Cloudflare')).toBeInTheDocument();
|
|
|
|
await waitFor(() => {
|
|
expect(screen.getByLabelText('API Token')).toBeInTheDocument();
|
|
});
|
|
});
|
|
|
|
it('handles form submission for creation', async () => {
|
|
const user = userEvent.setup();
|
|
render(<DNSProviderForm {...defaultProps} />);
|
|
|
|
await user.type(screen.getByLabelText('Provider Name'), 'New Provider');
|
|
|
|
const typeSelectTrigger = screen.getByRole('combobox', { name: 'Provider Type' });
|
|
await user.click(typeSelectTrigger);
|
|
|
|
// Select option by role to distinguish from trigger text
|
|
await user.click(screen.getByRole('option', { name: 'Cloudflare' }));
|
|
|
|
const tokenInput = await screen.findByLabelText('API Token');
|
|
await user.type(tokenInput, 'my-token');
|
|
|
|
mockCreateMutation.mutateAsync.mockResolvedValue({});
|
|
await user.click(screen.getByRole('button', { name: 'Create' }));
|
|
|
|
expect(mockCreateMutation.mutateAsync).toHaveBeenCalledWith(expect.objectContaining({
|
|
name: 'New Provider',
|
|
provider_type: 'cloudflare',
|
|
credentials: { api_token: 'my-token' },
|
|
}));
|
|
expect(defaultProps.onSuccess).toHaveBeenCalled();
|
|
});
|
|
|
|
it('handles validation failure (missing required fields)', async () => {
|
|
const user = userEvent.setup();
|
|
render(<DNSProviderForm {...defaultProps} />);
|
|
|
|
await user.type(screen.getByLabelText('Provider Name'), 'New Provider');
|
|
|
|
// Type is not selected, submit button should be disabled
|
|
const submitBtn = screen.getByRole('button', { name: 'Create' });
|
|
expect(submitBtn).toBeDisabled();
|
|
});
|
|
|
|
it('tests connection', async () => {
|
|
const user = userEvent.setup();
|
|
render(<DNSProviderForm {...defaultProps} />);
|
|
|
|
await user.type(screen.getByLabelText('Provider Name'), 'Test Prov');
|
|
await user.click(screen.getByRole('combobox', { name: 'Provider Type' }));
|
|
await user.click(screen.getByRole('option', { name: 'Cloudflare' }));
|
|
await user.type(screen.getByLabelText('API Token'), 'token');
|
|
|
|
mockTestCredentialsMutation.mutateAsync.mockResolvedValue({ success: true, message: 'Connection valid' });
|
|
|
|
await user.click(screen.getByRole('button', { name: 'Test Connection' }));
|
|
|
|
expect(mockTestCredentialsMutation.mutateAsync).toHaveBeenCalledWith(expect.objectContaining({
|
|
provider_type: 'cloudflare',
|
|
credentials: { api_token: 'token' }
|
|
}));
|
|
|
|
expect(await screen.findByText('Connection test successful')).toBeInTheDocument();
|
|
});
|
|
|
|
it('handles test connection failure', async () => {
|
|
const user = userEvent.setup();
|
|
render(<DNSProviderForm {...defaultProps} />);
|
|
|
|
await user.type(screen.getByLabelText('Provider Name'), 'Test Prov');
|
|
await user.click(screen.getByRole('combobox', { name: 'Provider Type' }));
|
|
await user.click(screen.getByRole('option', { name: 'Cloudflare' }));
|
|
await user.type(screen.getByLabelText('API Token'), 'token');
|
|
|
|
// Simulate error response structure
|
|
const errorResponse = {
|
|
response: { data: { error: 'Invalid token' } }
|
|
};
|
|
mockTestCredentialsMutation.mutateAsync.mockRejectedValue(errorResponse);
|
|
|
|
await user.click(screen.getByRole('button', { name: 'Test Connection' }));
|
|
|
|
expect(await screen.findByText('Connection test failed')).toBeInTheDocument();
|
|
expect(await screen.findByText('Invalid token')).toBeInTheDocument();
|
|
});
|
|
|
|
it('toggles advanced settings', async () => {
|
|
const user = userEvent.setup();
|
|
render(<DNSProviderForm {...defaultProps} />);
|
|
|
|
expect(screen.queryByLabelText('Propagation Timeout (seconds)')).not.toBeInTheDocument();
|
|
|
|
await user.click(screen.getByRole('button', { name: 'Advanced Settings' }));
|
|
|
|
expect(screen.getByLabelText('Propagation Timeout (seconds)')).toBeInTheDocument();
|
|
expect(screen.getByLabelText('Polling Interval (seconds)')).toBeInTheDocument();
|
|
expect(screen.getByLabelText('Set as default provider')).toBeInTheDocument();
|
|
});
|
|
});
|