Components added: - G-003: ErrorBoundary with withErrorBoundary HOC and PageErrorFallback - G-010: Pagination with SimplePagination variant - G-011: Dropdown system (Dropdown, DropdownItem, Menu, etc.) - G-012: FileUpload with drag & drop and preview - G-013: DatePicker with DateRangePicker Hooks added: - G-006: useDebounce, useDebouncedCallback, useDebounceWithImmediate - G-007: useLocalStorage, useSessionStorage Services added: - G-004: API Client with request/response interceptors, token refresh Tests: 49 passing (14 new tests for utility hooks) TypeScript: All types validated Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
184 lines
6.0 KiB
TypeScript
184 lines
6.0 KiB
TypeScript
/**
|
|
* Tests for Skeleton component
|
|
*/
|
|
|
|
import { describe, it, expect } from 'vitest';
|
|
import { render } from '@testing-library/react';
|
|
import {
|
|
Skeleton,
|
|
SkeletonText,
|
|
SkeletonCard,
|
|
SkeletonTable,
|
|
SkeletonAvatar,
|
|
SkeletonButton,
|
|
} from './Skeleton';
|
|
|
|
describe('Skeleton', () => {
|
|
describe('base Skeleton', () => {
|
|
it('should render with default props', () => {
|
|
const { container } = render(<Skeleton />);
|
|
const skeleton = container.firstChild as HTMLElement;
|
|
|
|
expect(skeleton).toBeInTheDocument();
|
|
expect(skeleton).toHaveClass('bg-gray-200', 'dark:bg-gray-700');
|
|
expect(skeleton).toHaveClass('animate-pulse');
|
|
expect(skeleton).toHaveClass('rounded'); // text variant default
|
|
});
|
|
|
|
it('should apply rectangular variant', () => {
|
|
const { container } = render(<Skeleton variant="rectangular" />);
|
|
const skeleton = container.firstChild as HTMLElement;
|
|
|
|
expect(skeleton).toHaveClass('rounded-md');
|
|
});
|
|
|
|
it('should apply circular variant', () => {
|
|
const { container } = render(<Skeleton variant="circular" />);
|
|
const skeleton = container.firstChild as HTMLElement;
|
|
|
|
expect(skeleton).toHaveClass('rounded-full');
|
|
});
|
|
|
|
it('should apply wave animation', () => {
|
|
const { container } = render(<Skeleton animation="wave" />);
|
|
const skeleton = container.firstChild as HTMLElement;
|
|
|
|
expect(skeleton).toHaveClass('animate-shimmer');
|
|
expect(skeleton).not.toHaveClass('animate-pulse');
|
|
});
|
|
|
|
it('should not animate with none', () => {
|
|
const { container } = render(<Skeleton animation="none" />);
|
|
const skeleton = container.firstChild as HTMLElement;
|
|
|
|
expect(skeleton).not.toHaveClass('animate-pulse');
|
|
expect(skeleton).not.toHaveClass('animate-shimmer');
|
|
});
|
|
|
|
it('should apply custom width and height as numbers', () => {
|
|
const { container } = render(<Skeleton width={100} height={50} />);
|
|
const skeleton = container.firstChild as HTMLElement;
|
|
|
|
expect(skeleton).toHaveStyle({ width: '100px', height: '50px' });
|
|
});
|
|
|
|
it('should apply custom width and height as strings', () => {
|
|
const { container } = render(<Skeleton width="50%" height="2rem" />);
|
|
const skeleton = container.firstChild as HTMLElement;
|
|
|
|
expect(skeleton).toHaveStyle({ width: '50%', height: '2rem' });
|
|
});
|
|
|
|
it('should be aria-hidden', () => {
|
|
const { container } = render(<Skeleton />);
|
|
const skeleton = container.firstChild as HTMLElement;
|
|
|
|
expect(skeleton).toHaveAttribute('aria-hidden', 'true');
|
|
});
|
|
|
|
it('should apply custom className', () => {
|
|
const { container } = render(<Skeleton className="my-custom-class" />);
|
|
const skeleton = container.firstChild as HTMLElement;
|
|
|
|
expect(skeleton).toHaveClass('my-custom-class');
|
|
});
|
|
});
|
|
|
|
describe('SkeletonText', () => {
|
|
it('should render default 3 lines', () => {
|
|
const { container } = render(<SkeletonText />);
|
|
const wrapper = container.firstChild as HTMLElement;
|
|
const lines = wrapper.querySelectorAll('[aria-hidden="true"]');
|
|
|
|
expect(lines).toHaveLength(3);
|
|
});
|
|
|
|
it('should render custom number of lines', () => {
|
|
const { container } = render(<SkeletonText lines={5} />);
|
|
const wrapper = container.firstChild as HTMLElement;
|
|
const lines = wrapper.querySelectorAll('[aria-hidden="true"]');
|
|
|
|
expect(lines).toHaveLength(5);
|
|
});
|
|
|
|
it('should apply gap classes', () => {
|
|
const { container } = render(<SkeletonText gap="lg" />);
|
|
const wrapper = container.firstChild as HTMLElement;
|
|
|
|
expect(wrapper).toHaveClass('gap-3');
|
|
});
|
|
});
|
|
|
|
describe('SkeletonCard', () => {
|
|
it('should render with header by default', () => {
|
|
const { container } = render(<SkeletonCard />);
|
|
const skeletons = container.querySelectorAll('[aria-hidden="true"]');
|
|
|
|
// Header (2 skeletons) + content lines (3) = 5
|
|
expect(skeletons.length).toBeGreaterThanOrEqual(5);
|
|
});
|
|
|
|
it('should render avatar when showAvatar is true', () => {
|
|
const { container } = render(<SkeletonCard showAvatar />);
|
|
const circular = container.querySelector('.rounded-full');
|
|
|
|
expect(circular).toBeInTheDocument();
|
|
});
|
|
|
|
it('should render footer when showFooter is true', () => {
|
|
const { container } = render(<SkeletonCard showFooter />);
|
|
const rectangles = container.querySelectorAll('.rounded-md');
|
|
|
|
expect(rectangles.length).toBeGreaterThanOrEqual(2);
|
|
});
|
|
});
|
|
|
|
describe('SkeletonTable', () => {
|
|
it('should render correct number of rows', () => {
|
|
const { container } = render(<SkeletonTable rows={3} columns={4} />);
|
|
const rows = container.querySelectorAll('.divide-y > div');
|
|
|
|
expect(rows).toHaveLength(3);
|
|
});
|
|
|
|
it('should render header when showHeader is true', () => {
|
|
const { container } = render(<SkeletonTable showHeader />);
|
|
const header = container.querySelector('.bg-background-subtle');
|
|
|
|
expect(header).toBeInTheDocument();
|
|
});
|
|
});
|
|
|
|
describe('SkeletonAvatar', () => {
|
|
it('should render with correct size', () => {
|
|
const { container } = render(<SkeletonAvatar size="lg" />);
|
|
const avatar = container.firstChild as HTMLElement;
|
|
|
|
expect(avatar).toHaveStyle({ width: '48px', height: '48px' });
|
|
});
|
|
|
|
it('should be circular', () => {
|
|
const { container } = render(<SkeletonAvatar />);
|
|
const avatar = container.firstChild as HTMLElement;
|
|
|
|
expect(avatar).toHaveClass('rounded-full');
|
|
});
|
|
});
|
|
|
|
describe('SkeletonButton', () => {
|
|
it('should render with correct height for size', () => {
|
|
const { container } = render(<SkeletonButton size="lg" />);
|
|
const button = container.firstChild as HTMLElement;
|
|
|
|
expect(button).toHaveStyle({ height: '48px' });
|
|
});
|
|
|
|
it('should apply custom width', () => {
|
|
const { container } = render(<SkeletonButton width={200} />);
|
|
const button = container.firstChild as HTMLElement;
|
|
|
|
expect(button).toHaveStyle({ width: '200px' });
|
|
});
|
|
});
|
|
});
|