feat(09-03): InspectTab UI + inspectCertAction + render tests
- Added inspectCertAction to actions.ts (JSON path for pemText, multipart path for file)
- Added CertDetails interface to actions.ts (mirrors API response shape)
- Implemented InspectTab: Analysieren button, loading state, grid-cols-2 result grid
- InspectTab handles wrong-password error (t('error.wrongPassword')) and generic error
- Added 2 new InspectTab tests: success grid (subject CN + SHA-256) and error (text-destructive)
- Fixed setup.ts: explicit expect.extend(matchers) for vitest@4.x compatibility
(Rule 1: @testing-library/jest-dom/vitest not extending global expect in vitest 4)
- Fixed existing test: getByText -> getAllByText for 'Analysieren' (now appears in tab nav + button)
- 9/9 cert-manager tests pass
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { cleanup, render, screen, fireEvent } from '@testing-library/react';
|
||||
import { cleanup, render, screen, fireEvent, waitFor, act } from '@testing-library/react';
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
// Mock next-intl — provide certManager namespace keys
|
||||
@@ -42,9 +42,12 @@ vi.mock('next-intl', () => ({
|
||||
}));
|
||||
|
||||
import CertManagerPage from './page';
|
||||
import { InspectTab } from './components/InspectTab';
|
||||
import * as actions from './actions';
|
||||
|
||||
afterEach(() => {
|
||||
cleanup();
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
describe('CertManagerPage shell', () => {
|
||||
@@ -56,7 +59,9 @@ describe('CertManagerPage shell', () => {
|
||||
|
||||
it('renders all four tab labels', () => {
|
||||
render(<CertManagerPage />);
|
||||
expect(screen.getByText('Analysieren')).toBeInTheDocument();
|
||||
// "Analysieren" appears as both tab label (nav) and InspectTab action button
|
||||
// getAllByText avoids the "multiple elements" error
|
||||
expect(screen.getAllByText('Analysieren').length).toBeGreaterThanOrEqual(1);
|
||||
expect(screen.getByText('Aufteilen')).toBeInTheDocument();
|
||||
expect(screen.getByText('Zusammenfuehren')).toBeInTheDocument();
|
||||
expect(screen.getByText('Konvertieren')).toBeInTheDocument();
|
||||
@@ -99,3 +104,86 @@ describe('CertManagerPage shell', () => {
|
||||
expect(screen.getByText('Lade eine Datei hoch und waehle ein Ausgabeformat.')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// InspectTab — unit tests for the Analysieren result grid and error state
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const MOCK_CERT_DETAILS: actions.CertDetails = {
|
||||
subject: { cn: 'example.com', o: 'Acme Corp', ou: 'IT', c: 'DE' },
|
||||
issuer: { cn: 'Example CA', o: 'CA Corp', c: 'US' },
|
||||
validity: {
|
||||
notBefore: '2025-01-01T00:00:00.000Z',
|
||||
notAfter: '2026-01-01T00:00:00.000Z',
|
||||
isExpired: false,
|
||||
daysLeft: 180,
|
||||
},
|
||||
san: ['example.com', 'www.example.com'],
|
||||
keyType: 'RSA',
|
||||
keyBits: 2048,
|
||||
serialNumber: '01',
|
||||
signatureAlgorithm: 'sha256WithRSAEncryption',
|
||||
fingerprint: {
|
||||
sha1: 'AA:BB:CC:DD:EE',
|
||||
sha256: 'FF:00:11:22:33:44:55:66:77:88',
|
||||
},
|
||||
pemPreview: '-----BEGIN CERTIFICATE-----\nXXX\n-----END CERTIFICATE-----',
|
||||
};
|
||||
|
||||
describe('InspectTab', () => {
|
||||
it('renders CertDetails grid with subject CN and SHA-256 after successful inspect', async () => {
|
||||
vi.spyOn(actions, 'inspectCertAction').mockResolvedValue(MOCK_CERT_DETAILS);
|
||||
|
||||
render(
|
||||
<InspectTab
|
||||
file={null}
|
||||
pemText="-----BEGIN CERTIFICATE-----\ntest\n-----END CERTIFICATE-----"
|
||||
password=""
|
||||
/>,
|
||||
);
|
||||
|
||||
// Click the Analysieren button
|
||||
fireEvent.click(screen.getByText('Analysieren'));
|
||||
|
||||
// Wait for the async action to complete and result grid to render
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('example.com')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
// SHA-256 fingerprint is rendered
|
||||
expect(screen.getByText('FF:00:11:22:33:44:55:66:77:88')).toBeInTheDocument();
|
||||
|
||||
// Empty state is no longer shown
|
||||
expect(screen.queryByText('Kein Zertifikat geladen.')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows text-destructive error when inspectCertAction rejects', async () => {
|
||||
vi.spyOn(actions, 'inspectCertAction').mockRejectedValue(
|
||||
new Error('wrong password'),
|
||||
);
|
||||
|
||||
render(
|
||||
<InspectTab
|
||||
file={null}
|
||||
pemText="test pem"
|
||||
password="wrong"
|
||||
/>,
|
||||
);
|
||||
|
||||
fireEvent.click(screen.getByText('Analysieren'));
|
||||
|
||||
// Wait for the error message to appear
|
||||
await waitFor(() => {
|
||||
// The wrong-password error message should be rendered
|
||||
expect(
|
||||
screen.getByText('Falsches Passwort. PFX/P12-Datei konnte nicht entschluesselt werden.'),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
// Error element should have the destructive class
|
||||
const errorEl = screen.getByText(
|
||||
'Falsches Passwort. PFX/P12-Datei konnte nicht entschluesselt werden.',
|
||||
);
|
||||
expect(errorEl.className).toContain('text-destructive');
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user