jest.mock('../../../src/renderer/lib/utils', () => ({
cn: (...classes: string[]) => classes.join(' '),
}));
jest.mock('../../../src/renderer/components/ui/skeleton', () => {
const React = require('react');
const { cn } = require('../../../src/renderer/lib/utils');
const Skeleton = ({ className, ...props }: React.ComponentProps<'div'>) => {
return React.createElement('div', {
'data-slot': 'skeleton',
className: cn('bg-accent animate-pulse rounded-md', className),
'data-testid': 'skeleton',
...props,
});
};
return { Skeleton };
});
import React from 'react';
import { render, screen } from '@testing-library/react';
import '@testing-library/jest-dom';
import { Skeleton } from '../../../src/renderer/components/ui/skeleton';
describe('Skeleton Component', () => {
beforeEach(() => {
jest.clearAllMocks();
});
describe('Basic Rendering', () => {
test('renders with default props', () => {
render();
const skeleton = screen.getByTestId('skeleton');
expect(skeleton).toBeInTheDocument();
expect(skeleton).toHaveClass('bg-accent', 'animate-pulse', 'rounded-md');
});
test('renders with custom className', () => {
render();
const skeleton = screen.getByTestId('skeleton');
expect(skeleton).toHaveClass('custom-skeleton');
});
test('forwards additional props', () => {
render();
const skeleton = screen.getByTestId('custom-skeleton');
expect(skeleton).toHaveAttribute('id', 'skeleton-123');
});
test('applies default styling', () => {
render();
const skeleton = screen.getByTestId('skeleton');
expect(skeleton).toHaveClass('bg-accent');
expect(skeleton).toHaveClass('animate-pulse');
expect(skeleton).toHaveClass('rounded-md');
});
});
describe('Styling and Classes', () => {
test('merges custom classes with default classes', () => {
render();
const skeleton = screen.getByTestId('skeleton');
expect(skeleton).toHaveClass(
'bg-accent',
'animate-pulse',
'rounded-md',
'w-32',
'h-8'
);
});
test('handles empty className', () => {
render();
const skeleton = screen.getByTestId('skeleton');
expect(skeleton).toHaveClass('bg-accent', 'animate-pulse', 'rounded-md');
});
test('handles undefined className', () => {
render();
const skeleton = screen.getByTestId('skeleton');
expect(skeleton).toHaveClass('bg-accent', 'animate-pulse', 'rounded-md');
});
});
describe('Accessibility', () => {
test('has correct data-slot attribute', () => {
render();
const skeleton = screen.getByTestId('skeleton');
expect(skeleton).toHaveAttribute('data-slot', 'skeleton');
});
test('supports aria attributes', () => {
render();
const skeleton = screen.getByTestId('skeleton');
expect(skeleton).toHaveAttribute('aria-label', 'Loading content');
});
test('supports role attribute', () => {
render();
const skeleton = screen.getByTestId('skeleton');
expect(skeleton).toHaveAttribute('role', 'presentation');
});
test('forwards all standard HTML div props', () => {
render(
);
const skeleton = screen.getByTestId('skeleton');
expect(skeleton).toHaveAttribute('title', 'Loading...');
expect(skeleton).toHaveAttribute('tabIndex', '-1');
expect(skeleton).toHaveAttribute('aria-live', 'polite');
expect(skeleton).toHaveAttribute('aria-describedby', 'loading-desc');
});
});
describe('Animation', () => {
test('has pulse animation by default', () => {
render();
const skeleton = screen.getByTestId('skeleton');
expect(skeleton).toHaveClass('animate-pulse');
});
test('can be used with custom animations', () => {
render();
const skeleton = screen.getByTestId('skeleton');
expect(skeleton).toHaveClass('animate-pulse', 'animate-bounce');
});
});
describe('Content and Children', () => {
test('renders without children', () => {
render();
const skeleton = screen.getByTestId('skeleton');
expect(skeleton).toBeEmptyDOMElement();
});
test('renders with children', () => {
render(
Loading...
);
const skeleton = screen.getByTestId('skeleton');
expect(skeleton).toHaveTextContent('Loading...');
});
test('renders with complex children', () => {
render(
Loading content
Please wait...
);
const skeleton = screen.getByTestId('skeleton');
expect(skeleton).toHaveTextContent('Loading content');
expect(skeleton).toHaveTextContent('Please wait...');
});
});
describe('Sizing and Layout', () => {
test('can be used for different sizes', () => {
render();
const skeleton = screen.getByTestId('skeleton');
expect(skeleton).toHaveClass('w-full', 'h-4');
});
test('can be used for avatar placeholders', () => {
render();
const skeleton = screen.getByTestId('skeleton');
expect(skeleton).toHaveClass('w-10', 'h-10', 'rounded-full');
});
test('can be used for text line placeholders', () => {
render();
const skeleton = screen.getByTestId('skeleton');
expect(skeleton).toHaveClass('h-4', 'w-3/4');
});
test('can be used for card placeholders', () => {
render();
const skeleton = screen.getByTestId('skeleton');
expect(skeleton).toHaveClass('h-32', 'w-full', 'rounded-lg');
});
});
describe('Edge Cases and Error Conditions', () => {
test('handles null props gracefully', () => {
render();
const skeleton = screen.getByTestId('skeleton');
expect(skeleton).toBeInTheDocument();
});
test('handles undefined props gracefully', () => {
render();
const skeleton = screen.getByTestId('skeleton');
expect(skeleton).toBeInTheDocument();
});
test('handles empty props object', () => {
render();
const skeleton = screen.getByTestId('skeleton');
expect(skeleton).toBeInTheDocument();
});
});
describe('Integration Scenarios', () => {
test('can be used as a loading placeholder for text', () => {
render(
Article Title
);
const skeletons = screen.getAllByTestId('skeleton');
expect(skeletons).toHaveLength(3);
});
test('can be used as a loading placeholder for cards', () => {
render(
{[1, 2, 3].map((i) => (
))}
);
const skeletons = screen.getAllByTestId('skeleton');
expect(skeletons).toHaveLength(9); // 3 cards × 3 skeletons each
});
test('can be used as a loading placeholder for user profiles', () => {
render(
);
const skeletons = screen.getAllByTestId('skeleton');
expect(skeletons).toHaveLength(3);
});
test('can be used for loading states in forms', () => {
render(
);
const skeletons = screen.getAllByTestId('skeleton');
expect(skeletons).toHaveLength(5);
});
test('can be used for table loading states', () => {
render(
| Name |
Email |
Status |
{[1, 2, 3].map((i) => (
|
|
|
|
))}
);
const skeletons = screen.getAllByTestId('skeleton');
expect(skeletons).toHaveLength(9); // 3 rows × 3 columns
});
});
describe('Real-world Usage Patterns', () => {
test('loading state for a blog post', () => {
render(
);
const skeletons = screen.getAllByTestId('skeleton');
expect(skeletons).toHaveLength(6);
});
test('loading state for a product card', () => {
render(
);
const skeletons = screen.getAllByTestId('skeleton');
expect(skeletons).toHaveLength(5);
});
test('loading state for a chat message', () => {
render(
);
const skeletons = screen.getAllByTestId('skeleton');
expect(skeletons).toHaveLength(5);
});
test('loading state for a data table with pagination', () => {
render(
{[1, 2, 3, 4, 5].map((i) => (
|
|
|
|
|
))}
);
const skeletons = screen.getAllByTestId('skeleton');
expect(skeletons).toHaveLength(23); // 5 rows × 4 columns + 3 pagination buttons
});
});
describe('Performance and Best Practices', () => {
test('renders efficiently with many instances', () => {
render(
{Array.from({ length: 50 }, (_, i) => (
))}
);
const skeletons = screen.getAllByTestId('skeleton');
expect(skeletons).toHaveLength(50);
});
test('maintains performance with deep nesting', () => {
render(
);
const skeleton = screen.getByTestId('skeleton');
expect(skeleton).toBeInTheDocument();
});
test('works well with conditional rendering', () => {
const { rerender } = render();
expect(screen.getByTestId('skeleton')).toBeInTheDocument();
rerender(No skeleton
);
expect(screen.queryByTestId('skeleton')).not.toBeInTheDocument();
rerender();
expect(screen.getByTestId('skeleton')).toBeInTheDocument();
});
});
describe('TypeScript and Prop Forwarding', () => {
test('accepts all standard HTML div props', () => {
render(
{}}
onMouseEnter={() => {}}
style={{ margin: '10px' }}
data-custom='test'
/>
);
const skeleton = screen.getByTestId('skeleton');
expect(skeleton).toBeInTheDocument();
expect(skeleton).toHaveAttribute('data-custom', 'test');
});
test('works with React refs', () => {
const ref = React.createRef();
render();
expect(ref.current).toBeInstanceOf(HTMLDivElement);
});
test('supports all React.ComponentProps', () => {
render(
);
const skeleton = screen.getByTestId('skeleton');
expect(skeleton).toBeInTheDocument();
expect(skeleton).toHaveAttribute('tabIndex', '0');
expect(skeleton).toHaveAttribute('title', 'Skeleton');
});
});
});