import { describe, it, expect } from 'vitest' import { render, screen } from '@testing-library/react' import { CharonLoader, CharonCoinLoader, CerberusLoader, ConfigReloadOverlay, } from '../LoadingStates' describe('LoadingStates - Security Audit', () => { describe('CharonLoader', () => { it('renders without crashing', () => { const { container } = render() expect(container.querySelector('svg')).toBeInTheDocument() }) it('handles all size variants', () => { const { rerender } = render() expect(screen.getByRole('status')).toBeInTheDocument() rerender() expect(screen.getByRole('status')).toBeInTheDocument() rerender() expect(screen.getByRole('status')).toBeInTheDocument() }) it('has accessible role and label', () => { render() const status = screen.getByRole('status') expect(status).toHaveAttribute('aria-label', 'Loading') }) it('applies correct size classes', () => { const { container, rerender } = render() expect(container.firstChild).toHaveClass('w-12', 'h-12') rerender() expect(container.firstChild).toHaveClass('w-20', 'h-20') rerender() expect(container.firstChild).toHaveClass('w-28', 'h-28') }) }) describe('CharonCoinLoader', () => { it('renders without crashing', () => { const { container } = render() expect(container.querySelector('svg')).toBeInTheDocument() }) it('has accessible role and label for authentication', () => { render() const status = screen.getByRole('status') expect(status).toHaveAttribute('aria-label', 'Authenticating') }) it('renders gradient definition', () => { const { container } = render() const gradient = container.querySelector('#goldGradient') expect(gradient).toBeInTheDocument() }) it('applies correct size classes', () => { const { container, rerender } = render() expect(container.firstChild).toHaveClass('w-12', 'h-12') rerender() expect(container.firstChild).toHaveClass('w-20', 'h-20') rerender() expect(container.firstChild).toHaveClass('w-28', 'h-28') }) }) describe('CerberusLoader', () => { it('renders without crashing', () => { const { container } = render() expect(container.querySelector('svg')).toBeInTheDocument() }) it('has accessible role and label for security', () => { render() const status = screen.getByRole('status') expect(status).toHaveAttribute('aria-label', 'Security Loading') }) it('renders three heads (three circles for heads)', () => { const { container } = render() const circles = container.querySelectorAll('circle') // At least 3 head circles should exist (plus paws and eyes) expect(circles.length).toBeGreaterThanOrEqual(3) }) it('applies correct size classes', () => { const { container, rerender } = render() expect(container.firstChild).toHaveClass('w-12', 'h-12') rerender() expect(container.firstChild).toHaveClass('w-20', 'h-20') rerender() expect(container.firstChild).toHaveClass('w-28', 'h-28') }) }) describe('ConfigReloadOverlay - XSS Protection', () => { it('renders with default props', () => { render() expect(screen.getByText('Ferrying configuration...')).toBeInTheDocument() expect(screen.getByText('Charon is crossing the Styx')).toBeInTheDocument() }) it('ATTACK: prevents XSS in message prop', () => { const xssPayload = '' render() // React should escape this automatically expect(screen.getByText(xssPayload)).toBeInTheDocument() expect(document.querySelector('script')).not.toBeInTheDocument() }) it('ATTACK: prevents XSS in submessage prop', () => { const xssPayload = '' render() expect(screen.getByText(xssPayload)).toBeInTheDocument() expect(document.querySelector('img[onerror]')).not.toBeInTheDocument() }) it('ATTACK: handles extremely long messages', () => { const longMessage = 'A'.repeat(10000) const { container } = render() // Should render without crashing expect(container).toBeInTheDocument() expect(screen.getByText(longMessage)).toBeInTheDocument() }) it('ATTACK: handles special characters', () => { const specialChars = '!@#$%^&*()_+-=[]{}|;:",.<>?/~`' render( ) expect(screen.getAllByText(specialChars)).toHaveLength(2) }) it('ATTACK: handles unicode and emoji', () => { const unicode = '🔥💀🐕‍🦺 λ µ π Σ 中文 العربية עברית' render() expect(screen.getByText(unicode)).toBeInTheDocument() }) it('renders correct theme - charon (blue)', () => { const { container } = render() const overlay = container.querySelector('.bg-blue-950\\/90') expect(overlay).toBeInTheDocument() }) it('renders correct theme - coin (gold)', () => { const { container } = render() const overlay = container.querySelector('.bg-amber-950\\/90') expect(overlay).toBeInTheDocument() }) it('renders correct theme - cerberus (red)', () => { const { container } = render() const overlay = container.querySelector('.bg-red-950\\/90') expect(overlay).toBeInTheDocument() }) it('applies correct z-index (z-50)', () => { const { container } = render() const overlay = container.querySelector('.z-50') expect(overlay).toBeInTheDocument() }) it('applies backdrop blur', () => { const { container } = render() const backdrop = container.querySelector('.backdrop-blur-sm') expect(backdrop).toBeInTheDocument() }) it('ATTACK: type prop injection attempt', () => { // @ts-expect-error - Testing invalid type const { container } = render() // Should default to charon theme expect(container.querySelector('.bg-blue-950\\/90')).toBeInTheDocument() }) }) describe('Overlay Integration Tests', () => { it('CharonLoader renders inside overlay', () => { render() expect(screen.getByRole('status')).toHaveAttribute('aria-label', 'Loading') }) it('CharonCoinLoader renders inside overlay', () => { render() expect(screen.getByRole('status')).toHaveAttribute('aria-label', 'Authenticating') }) it('CerberusLoader renders inside overlay', () => { render() expect(screen.getByRole('status')).toHaveAttribute('aria-label', 'Security Loading') }) }) describe('CSS Animation Requirements', () => { it('CharonLoader uses animate-bob-boat class', () => { const { container } = render() const animated = container.querySelector('.animate-bob-boat') expect(animated).toBeInTheDocument() }) it('CharonCoinLoader uses animate-spin-y class', () => { const { container } = render() const animated = container.querySelector('.animate-spin-y') expect(animated).toBeInTheDocument() }) it('CerberusLoader uses animate-rotate-head class', () => { const { container } = render() const animated = container.querySelector('.animate-rotate-head') expect(animated).toBeInTheDocument() }) }) describe('Edge Cases', () => { it('handles undefined size prop gracefully', () => { const { container } = render() expect(container.firstChild).toHaveClass('w-20', 'h-20') // defaults to md }) it('handles null message', () => { // @ts-expect-error - Testing null render() // Null message renders as empty paragraph - component gracefully handles null const textContainer = screen.getByText(/Charon is crossing the Styx/i).closest('div') expect(textContainer).toBeInTheDocument() }) it('handles empty string message', () => { render() // Should render but be empty expect(screen.queryByText('Ferrying configuration...')).not.toBeInTheDocument() }) it('handles undefined type prop', () => { const { container } = render() // Should default to charon expect(container.querySelector('.bg-blue-950\\/90')).toBeInTheDocument() }) }) describe('Accessibility Requirements', () => { it('overlay is keyboard accessible', () => { const { container } = render() const overlay = container.firstChild expect(overlay).toBeInTheDocument() }) it('all loaders have status role', () => { render( <> ) const statuses = screen.getAllByRole('status') expect(statuses).toHaveLength(3) }) it('all loaders have aria-label', () => { const { container: c1 } = render() const { container: c2 } = render() const { container: c3 } = render() expect(c1.firstChild).toHaveAttribute('aria-label') expect(c2.firstChild).toHaveAttribute('aria-label') expect(c3.firstChild).toHaveAttribute('aria-label') }) }) describe('Performance Tests', () => { it('renders CharonLoader quickly', () => { const start = performance.now() render() const end = performance.now() expect(end - start).toBeLessThan(100) // Should render in <100ms }) it('renders CharonCoinLoader quickly', () => { const start = performance.now() render() const end = performance.now() expect(end - start).toBeLessThan(100) }) it('renders CerberusLoader quickly', () => { const start = performance.now() render() const end = performance.now() expect(end - start).toBeLessThan(100) }) it('renders ConfigReloadOverlay quickly', () => { const start = performance.now() render() const end = performance.now() expect(end - start).toBeLessThan(100) }) }) })