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%
72 lines
2.5 KiB
TypeScript
72 lines
2.5 KiB
TypeScript
import { render, screen } from '@testing-library/react'
|
|
import { describe, it, expect, vi } from 'vitest'
|
|
|
|
import type { ChainEntry } from '../../api/certificates'
|
|
import CertificateChainViewer from '../CertificateChainViewer'
|
|
|
|
vi.mock('react-i18next', () => ({
|
|
useTranslation: () => ({
|
|
t: (key: string) => key,
|
|
i18n: { language: 'en', changeLanguage: vi.fn() },
|
|
}),
|
|
}))
|
|
|
|
function makeChain(count: number): ChainEntry[] {
|
|
return Array.from({ length: count }, (_, i) => ({
|
|
subject: `Subject ${i}`,
|
|
issuer: `Issuer ${i}`,
|
|
expires_at: '2026-06-01T00:00:00Z',
|
|
}))
|
|
}
|
|
|
|
describe('CertificateChainViewer', () => {
|
|
it('renders empty state when chain is empty', () => {
|
|
render(<CertificateChainViewer chain={[]} />)
|
|
expect(screen.getByText('certificates.noChainData')).toBeTruthy()
|
|
})
|
|
|
|
it('renders single entry as leaf', () => {
|
|
render(<CertificateChainViewer chain={makeChain(1)} />)
|
|
expect(screen.getByText('certificates.chainLeaf')).toBeTruthy()
|
|
expect(screen.getByText('Subject 0')).toBeTruthy()
|
|
})
|
|
|
|
it('renders two entries as leaf + root', () => {
|
|
render(<CertificateChainViewer chain={makeChain(2)} />)
|
|
expect(screen.getByText('certificates.chainLeaf')).toBeTruthy()
|
|
expect(screen.getByText('certificates.chainRoot')).toBeTruthy()
|
|
})
|
|
|
|
it('renders three entries as leaf + intermediate + root', () => {
|
|
render(<CertificateChainViewer chain={makeChain(3)} />)
|
|
expect(screen.getByText('certificates.chainLeaf')).toBeTruthy()
|
|
expect(screen.getByText('certificates.chainIntermediate')).toBeTruthy()
|
|
expect(screen.getByText('certificates.chainRoot')).toBeTruthy()
|
|
})
|
|
|
|
it('displays issuer for each entry', () => {
|
|
render(<CertificateChainViewer chain={makeChain(2)} />)
|
|
expect(screen.getByText(/Issuer 0/)).toBeTruthy()
|
|
expect(screen.getByText(/Issuer 1/)).toBeTruthy()
|
|
})
|
|
|
|
it('displays formatted expiration dates', () => {
|
|
render(<CertificateChainViewer chain={makeChain(1)} />)
|
|
const dateStr = new Date('2026-06-01T00:00:00Z').toLocaleDateString()
|
|
expect(screen.getByText(new RegExp(dateStr))).toBeTruthy()
|
|
})
|
|
|
|
it('uses list role with list items', () => {
|
|
render(<CertificateChainViewer chain={makeChain(2)} />)
|
|
expect(screen.getByRole('list')).toBeTruthy()
|
|
expect(screen.getAllByRole('listitem')).toHaveLength(2)
|
|
})
|
|
|
|
it('has aria-label on list', () => {
|
|
render(<CertificateChainViewer chain={makeChain(1)} />)
|
|
expect(screen.getByRole('list').getAttribute('aria-label')).toBe(
|
|
'certificates.certificateChain',
|
|
)
|
|
})
|
|
})
|