Add comprehensive unit tests for the certificate upload, export, and detail management feature: - CertificateExportDialog: 21 tests covering format selection, blob download, error handling, and password-protected exports - CertificateUploadDialog: 23 tests covering file validation, format detection, drag-and-drop, and upload flow - CertificateDetailDialog: 19 tests covering detail display, loading state, missing fields, and branch coverage - CertificateChainViewer: 8 tests covering chain visualization - CertificateValidationPreview: 16 tests covering validation display - FileDropZone: 18 tests covering drag-and-drop interactions - useCertificates hooks: 10 tests covering all React Query hooks - certificates API: 7 new tests for previously uncovered endpoints Fix null-safety issue in ProxyHosts where cert.domains could be undefined, causing a runtime error on split(). Frontend patch coverage: 90.6%, overall lines: 89.09%
136 lines
4.5 KiB
TypeScript
136 lines
4.5 KiB
TypeScript
import { render, screen } from '@testing-library/react'
|
|
import { describe, it, expect, vi } from 'vitest'
|
|
|
|
import type { ValidationResult } from '../../api/certificates'
|
|
import CertificateValidationPreview from '../CertificateValidationPreview'
|
|
|
|
vi.mock('react-i18next', () => ({
|
|
useTranslation: () => ({
|
|
t: (key: string) => key,
|
|
i18n: { language: 'en', changeLanguage: vi.fn() },
|
|
}),
|
|
}))
|
|
|
|
function makeResult(overrides: Partial<ValidationResult> = {}): ValidationResult {
|
|
return {
|
|
valid: true,
|
|
common_name: 'example.com',
|
|
domains: ['example.com', 'www.example.com'],
|
|
issuer_org: 'Test CA',
|
|
expires_at: '2026-06-01T00:00:00Z',
|
|
key_match: true,
|
|
chain_valid: true,
|
|
chain_depth: 2,
|
|
warnings: [],
|
|
errors: [],
|
|
...overrides,
|
|
}
|
|
}
|
|
|
|
describe('CertificateValidationPreview', () => {
|
|
it('renders valid certificate state', () => {
|
|
render(<CertificateValidationPreview result={makeResult()} />)
|
|
expect(screen.getByText('certificates.validCertificate')).toBeTruthy()
|
|
expect(screen.getByTestId('certificate-validation-preview')).toBeTruthy()
|
|
})
|
|
|
|
it('renders invalid certificate state', () => {
|
|
render(
|
|
<CertificateValidationPreview result={makeResult({ valid: false })} />,
|
|
)
|
|
expect(screen.getByText('certificates.invalidCertificate')).toBeTruthy()
|
|
})
|
|
|
|
it('displays common name', () => {
|
|
render(<CertificateValidationPreview result={makeResult()} />)
|
|
expect(screen.getByText('example.com')).toBeTruthy()
|
|
})
|
|
|
|
it('displays domains joined by comma', () => {
|
|
render(<CertificateValidationPreview result={makeResult()} />)
|
|
expect(screen.getByText('example.com, www.example.com')).toBeTruthy()
|
|
})
|
|
|
|
it('displays dash when no domains provided', () => {
|
|
render(
|
|
<CertificateValidationPreview result={makeResult({ domains: [] })} />,
|
|
)
|
|
const dashes = screen.getAllByText('-')
|
|
expect(dashes.length).toBeGreaterThan(0)
|
|
})
|
|
|
|
it('displays issuer organization', () => {
|
|
render(<CertificateValidationPreview result={makeResult()} />)
|
|
expect(screen.getByText('Test CA')).toBeTruthy()
|
|
})
|
|
|
|
it('displays formatted expiration date', () => {
|
|
render(<CertificateValidationPreview result={makeResult()} />)
|
|
const dateStr = new Date('2026-06-01T00:00:00Z').toLocaleDateString()
|
|
expect(screen.getByText(dateStr)).toBeTruthy()
|
|
})
|
|
|
|
it('shows Yes for key match', () => {
|
|
render(<CertificateValidationPreview result={makeResult({ key_match: true, chain_valid: false })} />)
|
|
expect(screen.getByText('Yes')).toBeTruthy()
|
|
})
|
|
|
|
it('shows No key provided when no key match', () => {
|
|
render(
|
|
<CertificateValidationPreview result={makeResult({ key_match: false })} />,
|
|
)
|
|
expect(screen.getByText('No key provided')).toBeTruthy()
|
|
})
|
|
|
|
it('shows chain depth when > 0', () => {
|
|
render(
|
|
<CertificateValidationPreview result={makeResult({ chain_depth: 3 })} />,
|
|
)
|
|
expect(screen.getByText('3')).toBeTruthy()
|
|
})
|
|
|
|
it('does not show chain depth when 0', () => {
|
|
render(
|
|
<CertificateValidationPreview result={makeResult({ chain_depth: 0 })} />,
|
|
)
|
|
expect(screen.queryByText('certificates.chainDepth')).toBeFalsy()
|
|
})
|
|
|
|
it('renders warnings when present', () => {
|
|
render(
|
|
<CertificateValidationPreview
|
|
result={makeResult({ warnings: ['Expiring soon', 'Weak key'] })}
|
|
/>,
|
|
)
|
|
expect(screen.getByText('certificates.warnings')).toBeTruthy()
|
|
expect(screen.getByText('Expiring soon')).toBeTruthy()
|
|
expect(screen.getByText('Weak key')).toBeTruthy()
|
|
})
|
|
|
|
it('does not render warnings section when empty', () => {
|
|
render(<CertificateValidationPreview result={makeResult({ warnings: [] })} />)
|
|
expect(screen.queryByText('certificates.warnings')).toBeFalsy()
|
|
})
|
|
|
|
it('renders errors when present', () => {
|
|
render(
|
|
<CertificateValidationPreview
|
|
result={makeResult({ errors: ['Certificate revoked'] })}
|
|
/>,
|
|
)
|
|
expect(screen.getByText('certificates.errors')).toBeTruthy()
|
|
expect(screen.getByText('Certificate revoked')).toBeTruthy()
|
|
})
|
|
|
|
it('does not render errors section when empty', () => {
|
|
render(<CertificateValidationPreview result={makeResult({ errors: [] })} />)
|
|
expect(screen.queryByText('certificates.errors')).toBeFalsy()
|
|
})
|
|
|
|
it('has correct region role and aria-label', () => {
|
|
render(<CertificateValidationPreview result={makeResult()} />)
|
|
const region = screen.getByRole('region')
|
|
expect(region.getAttribute('aria-label')).toBe('certificates.validationPreview')
|
|
})
|
|
})
|