import React from 'react'; import { describe, it, expect, vi } from 'vitest'; import { render, fireEvent } from '@testing-library/react'; import { PixelRotate } from '../../animations/PixelRotate'; import { mockMatchMedia } from './matchmedia-mock'; /** * jsdom lacks native CSS animation support, so React registers the * vendor-prefixed `webkitAnimationEnd` event there. Dispatch both variants — * exactly one is registered, so the handler fires exactly once. */ function dispatchAnimationEnd(el: Element) { fireEvent.animationEnd(el); el.dispatchEvent(new Event('webkitAnimationEnd', { bubbles: true })); } describe('PixelRotate', () => { it('renders children inside the wrapper', () => { const { getByText } = render(spinner); expect(getByText('spinner')).toBeTruthy(); }); it('applies the pxl-rotate animation on mount with defaults', () => { const { container } = render(x); const el = container.firstElementChild as HTMLElement; expect(el.style.animation).toContain('pxl-rotate'); expect(el.style.animation).toContain('1800ms'); expect(el.style.animation).toContain('linear'); expect(el.style.animation).toContain('infinite'); }); it('reflects duration, repeat and direction props', () => { const { container } = render( x, ); const el = container.firstElementChild as HTMLElement; expect(el.style.animation).toContain('600ms'); expect(el.style.animation).toContain(' 2 '); expect(el.style.animationDirection).toBe('reverse'); }); it('does not animate when trigger={false} (controlled off)', () => { const { container } = render(x); const el = container.firstElementChild as HTMLElement; expect(el.style.animation).toBe(''); }); it('trigger="click" only animates after a click', () => { const { container } = render(x); const el = container.firstElementChild as HTMLElement; expect(el.style.animation).toBe(''); fireEvent.click(el); expect(el.style.animation).toContain('pxl-rotate'); }); it('fires onComplete when the animation ends on the wrapper', () => { const onComplete = vi.fn(); const { container } = render( x, ); dispatchAnimationEnd(container.firstElementChild as HTMLElement); expect(onComplete).toHaveBeenCalledTimes(1); }); it('renders children statically (no animation) when the user prefers reduced motion', () => { const ctl = mockMatchMedia(true); try { const { container, getByText } = render(calm); const el = container.firstElementChild as HTMLElement; expect(el.style.animation).toBe(''); expect(getByText('calm')).toBeTruthy(); } finally { ctl.restore(); } }); it('merges className with inline-block and injects keyframes once', () => { const { container } = render(x); const el = container.firstElementChild as HTMLElement; expect(el.className).toContain('inline-block'); expect(el.className).toContain('custom'); expect(document.querySelectorAll('#pxl-anims')).toHaveLength(1); }); });