test: add tests for ErrorDisplay, LoadingState, ComingSoon

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
senke 2026-02-12 22:19:54 +01:00
parent a68e776797
commit 24b2c2fb84
3 changed files with 283 additions and 0 deletions

View file

@ -0,0 +1,61 @@
import { describe, it, expect, vi } from 'vitest';
import { render, screen, fireEvent } from '@testing-library/react';
import { ComingSoon } from './ComingSoon';
// Mock i18n hook
vi.mock('@/hooks/useTranslation', () => ({
useTranslation: () => ({
t: (key: string) => {
const translations: Record<string, string> = {
'comingSoon.description': 'This feature is coming soon!',
'comingSoon.goBack': 'Go Back',
'comingSoon.notifyMe': 'Notify Me',
};
return translations[key] || key;
},
}),
}));
describe('ComingSoon', () => {
it('should render the feature name', () => {
render(<ComingSoon feature="Audio Streaming" />);
expect(screen.getByText('Audio Streaming')).toBeInTheDocument();
});
it('should render the description', () => {
render(<ComingSoon feature="Test Feature" />);
expect(screen.getByText('This feature is coming soon!')).toBeInTheDocument();
});
it('should render the Notify Me button (disabled)', () => {
render(<ComingSoon feature="Test" />);
const notifyButton = screen.getByText('Notify Me');
expect(notifyButton).toBeInTheDocument();
expect(notifyButton.closest('button')).toBeDisabled();
});
it('should render Go Back button when onGoBack is provided', () => {
const onGoBack = vi.fn();
render(<ComingSoon feature="Test" onGoBack={onGoBack} />);
const goBackButton = screen.getByText('Go Back');
expect(goBackButton).toBeInTheDocument();
fireEvent.click(goBackButton);
expect(onGoBack).toHaveBeenCalledTimes(1);
});
it('should not render Go Back button when onGoBack is not provided', () => {
render(<ComingSoon feature="Test" />);
expect(screen.queryByText('Go Back')).not.toBeInTheDocument();
});
it('should render the logo illustration', () => {
const { container } = render(<ComingSoon feature="Test" />);
expect(container.querySelector('svg')).toBeInTheDocument();
});
});

View file

@ -0,0 +1,102 @@
import { describe, it, expect, vi } from 'vitest';
import { render, screen, fireEvent } from '@testing-library/react';
import { ErrorDisplay } from './ErrorDisplay';
// Mock dependencies that ErrorDisplay uses internally
vi.mock('@/utils/toast', () => ({
default: { success: vi.fn(), error: vi.fn() },
}));
vi.mock('@/utils/reportIssue', () => ({
formatIssueReport: vi.fn(() => 'mock report'),
copyIssueReportToClipboard: vi.fn(),
openGitHubIssue: vi.fn(),
}));
describe('ErrorDisplay', () => {
it('should render with a string error', () => {
render(<ErrorDisplay error="Something went wrong" />);
expect(screen.getByRole('alert')).toBeInTheDocument();
expect(screen.getByText('Error')).toBeInTheDocument();
});
it('should render with an Error object', () => {
render(<ErrorDisplay error={new Error('Network failure')} />);
expect(screen.getByRole('alert')).toBeInTheDocument();
});
it('should render with custom title', () => {
render(<ErrorDisplay error="Oops" title="Custom Title" />);
expect(screen.getByText('Custom Title')).toBeInTheDocument();
});
it('should render retry button when onRetry is provided', () => {
const onRetry = vi.fn();
render(<ErrorDisplay error="Error" onRetry={onRetry} />);
const retryButton = screen.getByText('Retry');
expect(retryButton).toBeInTheDocument();
fireEvent.click(retryButton);
expect(onRetry).toHaveBeenCalledTimes(1);
});
it('should not render retry button when onRetry is not provided', () => {
render(<ErrorDisplay error="Error" />);
expect(screen.queryByText('Retry')).not.toBeInTheDocument();
});
it('should render dismiss button when dismissible', () => {
const onDismiss = vi.fn();
render(<ErrorDisplay error="Error" onDismiss={onDismiss} dismissible />);
const dismissButton = screen.getByLabelText('Dismiss error');
expect(dismissButton).toBeInTheDocument();
fireEvent.click(dismissButton);
expect(onDismiss).toHaveBeenCalledTimes(1);
});
it('should render with warning severity', () => {
render(<ErrorDisplay error="Warning message" severity="warning" />);
expect(screen.getByText('Warning')).toBeInTheDocument();
});
it('should render with info severity', () => {
render(<ErrorDisplay error="Info message" severity="info" />);
expect(screen.getByText('Information')).toBeInTheDocument();
});
it('should render custom actions', () => {
const onAction = vi.fn();
render(
<ErrorDisplay
error="Error"
actions={[{ label: 'Custom Action', onClick: onAction }]}
/>,
);
const actionButton = screen.getByText('Custom Action');
expect(actionButton).toBeInTheDocument();
fireEvent.click(actionButton);
expect(onAction).toHaveBeenCalledTimes(1);
});
it('should render with context-based title', () => {
render(
<ErrorDisplay error="Error" context={{ action: 'loading tracks' }} />,
);
expect(screen.getByText('Error loading tracks')).toBeInTheDocument();
});
it('should have aria-live polite attribute', () => {
render(<ErrorDisplay error="Error" />);
expect(screen.getByRole('alert')).toHaveAttribute('aria-live', 'polite');
});
});

View file

@ -0,0 +1,120 @@
import { describe, it, expect } from 'vitest';
import { render, screen } from '@testing-library/react';
import { LoadingState, LoadingStateWrapper } from './LoadingState';
describe('LoadingState', () => {
describe('spinner variant (default)', () => {
it('should render spinner when isLoading is true', () => {
render(<LoadingState isLoading />);
expect(screen.getByRole('status')).toBeInTheDocument();
});
it('should render default text', () => {
render(<LoadingState isLoading />);
expect(screen.getByText('Chargement...', { selector: 'p' })).toBeInTheDocument();
});
it('should render custom text', () => {
render(<LoadingState isLoading text="Loading data..." />);
expect(screen.getByText('Loading data...', { selector: 'p' })).toBeInTheDocument();
});
it('should render children when isLoading is false', () => {
render(
<LoadingState isLoading={false}>
<div>Content loaded</div>
</LoadingState>,
);
expect(screen.getByText('Content loaded')).toBeInTheDocument();
expect(screen.queryByRole('status')).not.toBeInTheDocument();
});
});
describe('inline variant', () => {
it('should render inline spinner with text', () => {
render(<LoadingState isLoading variant="inline" text="Saving..." />);
expect(screen.getByText('Saving...')).toBeInTheDocument();
});
});
describe('skeleton variant', () => {
it('should render skeleton placeholders', () => {
const { container } = render(<LoadingState isLoading variant="skeleton" />);
expect(container.querySelector('.animate-pulse')).toBeInTheDocument();
});
it('should render children as skeleton content', () => {
render(
<LoadingState isLoading variant="skeleton">
<div data-testid="skeleton-content">Skeleton child</div>
</LoadingState>,
);
expect(screen.getByTestId('skeleton-content')).toBeInTheDocument();
});
});
describe('minimal variant', () => {
it('should render minimal spinner with role status', () => {
render(<LoadingState isLoading variant="minimal" />);
expect(screen.getByRole('status')).toBeInTheDocument();
});
});
describe('showSkeleton prop', () => {
it('should show skeleton when showSkeleton is true', () => {
const { container } = render(<LoadingState showSkeleton variant="skeleton" />);
expect(container.querySelector('.animate-pulse')).toBeInTheDocument();
});
});
it('should apply custom className', () => {
const { container } = render(
<LoadingState isLoading className="custom-class" />,
);
expect(container.querySelector('.custom-class')).toBeInTheDocument();
});
});
describe('LoadingStateWrapper', () => {
it('should show loading state when isLoading is true', () => {
render(
<LoadingStateWrapper isLoading>
<div>Child content</div>
</LoadingStateWrapper>,
);
expect(screen.getByRole('status')).toBeInTheDocument();
expect(screen.queryByText('Child content')).not.toBeInTheDocument();
});
it('should show children when isLoading is false', () => {
render(
<LoadingStateWrapper isLoading={false}>
<div>Child content</div>
</LoadingStateWrapper>,
);
expect(screen.getByText('Child content')).toBeInTheDocument();
expect(screen.queryByRole('status')).not.toBeInTheDocument();
});
it('should support custom loading variant', () => {
render(
<LoadingStateWrapper isLoading loadingVariant="inline" text="Wait...">
<div>Child</div>
</LoadingStateWrapper>,
);
expect(screen.getByText('Wait...')).toBeInTheDocument();
});
});