erp-construccion-frontend-v2/web/src/components/common/Skeleton.test.tsx
Adrian Flores Cortes 816d591115 [GAPS] feat(components): Implement remaining frontend gaps G-003 to G-013
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>
2026-02-04 01:34:21 -06:00

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' });
});
});
});