import { describe, it, expect, vi, beforeEach } from 'vitest'; import { render, screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import ProxyHostForm from '../ProxyHostForm'; import type { ProxyHost } from '../../api/proxyHosts'; vi.mock('../../hooks/useRemoteServers', () => ({ useRemoteServers: vi.fn(() => ({ servers: [], isLoading: false, error: null, })), })); vi.mock('../../hooks/useDocker', () => ({ useDocker: vi.fn(() => ({ containers: [], isLoading: false, error: null, refetch: vi.fn(), })), })); vi.mock('../../hooks/useDomains', () => ({ useDomains: vi.fn(() => ({ domains: [{ uuid: 'domain-1', name: 'test.com' }], createDomain: vi.fn().mockResolvedValue({}), isLoading: false, error: null, })), })); vi.mock('../../hooks/useCertificates', () => ({ useCertificates: vi.fn(() => ({ certificates: [], isLoading: false, error: null, })), })); vi.mock('../../hooks/useDNSDetection', () => ({ useDetectDNSProvider: vi.fn(() => ({ mutateAsync: vi.fn(), isPending: false, data: undefined, reset: vi.fn(), })), })); vi.mock('../../hooks/useAccessLists', () => ({ useAccessLists: vi.fn(() => ({ data: [ { id: 1, uuid: 'acl-uuid-1', name: 'Office Network', description: 'Office IP range', type: 'whitelist', enabled: true, }, ], isLoading: false, error: null, })), })); vi.mock('../../hooks/useSecurityHeaders', () => ({ useSecurityHeaderProfiles: vi.fn(() => ({ data: [ { id: 1, uuid: 'profile-uuid-1', name: 'Basic Security', description: 'Basic security headers', is_preset: true, preset_type: 'basic', security_score: 60, }, { id: undefined, uuid: undefined, name: 'Malformed Custom', description: 'Should be skipped in options map', is_preset: false, preset_type: 'custom', security_score: 10, }, ], isLoading: false, error: null, })), })); vi.mock('../ui/Select', () => { const findText = (children: React.ReactNode): string => { if (typeof children === 'string') { return children; } if (Array.isArray(children)) { return children.map((child) => findText(child)).join(' '); } if (children && typeof children === 'object' && 'props' in children) { const node = children as { props?: { children?: React.ReactNode } }; return findText(node.props?.children); } return ''; }; const Select = ({ value, onValueChange, children }: { value?: string; onValueChange?: (value: string) => void; children?: React.ReactNode }) => { const text = findText(children); const isSecurityHeaders = text.includes('None (No Security Headers)'); return (
{isSecurityHeaders && ( <>
{value}
)} {children}
); }; const SelectTrigger = ({ children, ...rest }: React.ComponentProps<'button'>) => ; const SelectContent = ({ children }: { children?: React.ReactNode }) =>
{children}
; const SelectItem = ({ children }: { value: string; children?: React.ReactNode }) =>
{children}
; const SelectValue = () => ; return { Select, SelectTrigger, SelectContent, SelectItem, SelectValue, }; }); vi.stubGlobal('fetch', vi.fn(() => Promise.resolve({ json: () => Promise.resolve({ internal_ip: '127.0.0.1' }) }))); const createWrapper = () => { const queryClient = new QueryClient({ defaultOptions: { queries: { retry: false }, mutations: { retry: false }, }, }); return ({ children }: { children: React.ReactNode }) => ( {children} ); }; const fillRequiredFields = async () => { await userEvent.type(screen.getByLabelText(/^Name/), 'Coverage Host'); await userEvent.type(screen.getByLabelText(/Domain Names/), 'test.com'); await userEvent.type(screen.getByLabelText(/^Host$/), 'localhost'); await userEvent.clear(screen.getByLabelText(/^Port$/)); await userEvent.type(screen.getByLabelText(/^Port$/), '8080'); }; describe('ProxyHostForm token coverage branches', () => { const onCancel = vi.fn(); beforeEach(() => { vi.clearAllMocks(); }); it('normalizes prefixed and numeric-string security header IDs', async () => { const onSubmit = vi.fn<(data: Partial) => Promise>().mockResolvedValue(); const Wrapper = createWrapper(); const { rerender } = render( ); expect(screen.getByTestId('security-select-value')).toHaveTextContent('id:7'); rerender( ); expect(screen.getByTestId('security-select-value')).toHaveTextContent('id:12'); }); it('converts plain numeric and custom security tokens on submit', async () => { const onSubmit = vi.fn<(data: Partial) => Promise>().mockResolvedValue(); const Wrapper = createWrapper(); render( ); await fillRequiredFields(); await userEvent.click(screen.getByRole('button', { name: 'emit-security-plain-numeric' })); await userEvent.click(screen.getByRole('button', { name: /Save/i })); await waitFor(() => { expect(onSubmit).toHaveBeenCalledWith( expect.objectContaining({ security_header_profile_id: 42 }) ); }); onSubmit.mockClear(); await userEvent.click(screen.getByRole('button', { name: 'emit-security-custom' })); await userEvent.click(screen.getByRole('button', { name: /Save/i })); await waitFor(() => { expect(onSubmit).toHaveBeenCalledWith( expect.objectContaining({ security_header_profile_id: 'custom-header-token' }) ); }); }); });