9d9bc5f22f
- Fix handleRotate bug ignoring Result from rotateSealedSecret() - Fix dead code branch in useControllerHealth - Replace all `any` types with `unknown` + type guards - Delete unused functions/exports (452 lines removed) - Add 18 new test files covering all hooks, libs, and components - 233 tests passing, zero tsc errors, zero lint issues Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
151 lines
4.2 KiB
TypeScript
151 lines
4.2 KiB
TypeScript
/**
|
|
* Unit tests for ErrorBoundary components
|
|
*/
|
|
|
|
import { fireEvent, render, screen } from '@testing-library/react';
|
|
import React from 'react';
|
|
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
|
|
// Mock MUI and iconify
|
|
vi.mock('@iconify/react', () => ({
|
|
Icon: ({ icon }: { icon: string }) => <span data-testid="icon">{icon}</span>,
|
|
}));
|
|
|
|
import { ApiErrorBoundary, GenericErrorBoundary } from './ErrorBoundary';
|
|
|
|
// Suppress console.error from error boundaries in tests
|
|
const originalError = console.error;
|
|
beforeEach(() => {
|
|
console.error = vi.fn();
|
|
});
|
|
|
|
afterEach(() => {
|
|
console.error = originalError;
|
|
});
|
|
|
|
function ThrowingComponent({ error }: { error: Error }): React.ReactNode {
|
|
throw error;
|
|
}
|
|
|
|
function GoodComponent() {
|
|
return <div>Working fine</div>;
|
|
}
|
|
|
|
describe('ErrorBoundary', () => {
|
|
describe('ApiErrorBoundary', () => {
|
|
it('should render children when no error', () => {
|
|
render(
|
|
<ApiErrorBoundary>
|
|
<GoodComponent />
|
|
</ApiErrorBoundary>
|
|
);
|
|
|
|
expect(screen.getByText('Working fine')).toBeDefined();
|
|
});
|
|
|
|
it('should catch errors and show API error UI', () => {
|
|
render(
|
|
<ApiErrorBoundary>
|
|
<ThrowingComponent error={new Error('API connection failed')} />
|
|
</ApiErrorBoundary>
|
|
);
|
|
|
|
expect(screen.getByText('API Communication Error')).toBeDefined();
|
|
expect(screen.getByText(/API connection failed/)).toBeDefined();
|
|
});
|
|
|
|
it('should show retry button that resets error', () => {
|
|
render(
|
|
<ApiErrorBoundary>
|
|
<ThrowingComponent error={new Error('test error')} />
|
|
</ApiErrorBoundary>
|
|
);
|
|
|
|
expect(screen.getByText('API Communication Error')).toBeDefined();
|
|
|
|
// Click retry
|
|
fireEvent.click(screen.getByText('Retry'));
|
|
|
|
// After reset, it will try to render children again (which will throw again)
|
|
// The boundary should catch it again
|
|
expect(screen.getByText('API Communication Error')).toBeDefined();
|
|
});
|
|
|
|
it('should render custom fallback if provided', () => {
|
|
render(
|
|
<ApiErrorBoundary fallback={<div>Custom fallback</div>}>
|
|
<ThrowingComponent error={new Error('error')} />
|
|
</ApiErrorBoundary>
|
|
);
|
|
|
|
expect(screen.getByText('Custom fallback')).toBeDefined();
|
|
});
|
|
|
|
it('should call onReset when retry is clicked', () => {
|
|
const onReset = vi.fn();
|
|
render(
|
|
<ApiErrorBoundary onReset={onReset}>
|
|
<ThrowingComponent error={new Error('error')} />
|
|
</ApiErrorBoundary>
|
|
);
|
|
|
|
fireEvent.click(screen.getByText('Retry'));
|
|
expect(onReset).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it('should show guidance about troubleshooting', () => {
|
|
render(
|
|
<ApiErrorBoundary>
|
|
<ThrowingComponent error={new Error('error')} />
|
|
</ApiErrorBoundary>
|
|
);
|
|
|
|
expect(screen.getByText(/Kubernetes cluster is accessible/)).toBeDefined();
|
|
expect(screen.getByText(/Sealed Secrets controller is running/)).toBeDefined();
|
|
});
|
|
});
|
|
|
|
describe('GenericErrorBoundary', () => {
|
|
it('should render children when no error', () => {
|
|
render(
|
|
<GenericErrorBoundary>
|
|
<GoodComponent />
|
|
</GenericErrorBoundary>
|
|
);
|
|
|
|
expect(screen.getByText('Working fine')).toBeDefined();
|
|
});
|
|
|
|
it('should catch errors and show generic error UI', () => {
|
|
render(
|
|
<GenericErrorBoundary>
|
|
<ThrowingComponent error={new Error('Unexpected error')} />
|
|
</GenericErrorBoundary>
|
|
);
|
|
|
|
expect(screen.getByText('Something Went Wrong')).toBeDefined();
|
|
expect(screen.getByText(/Unexpected error/)).toBeDefined();
|
|
});
|
|
|
|
it('should show reload button', () => {
|
|
render(
|
|
<GenericErrorBoundary>
|
|
<ThrowingComponent error={new Error('error')} />
|
|
</GenericErrorBoundary>
|
|
);
|
|
|
|
expect(screen.getByText('Reload')).toBeDefined();
|
|
});
|
|
|
|
it('should render custom fallback', () => {
|
|
render(
|
|
<GenericErrorBoundary fallback={<div>Custom error view</div>}>
|
|
<ThrowingComponent error={new Error('error')} />
|
|
</GenericErrorBoundary>
|
|
);
|
|
|
|
expect(screen.getByText('Custom error view')).toBeDefined();
|
|
});
|
|
});
|
|
});
|