import { describe, expect, it, vi, afterEach } from 'vitest'; import { reveal } from './reveal'; class TestIntersectionObserver { static instances: TestIntersectionObserver[] = []; callback: IntersectionObserverCallback; disconnect = vi.fn(); observe = vi.fn(); unobserve = vi.fn(); constructor(callback: IntersectionObserverCallback) { this.callback = callback; TestIntersectionObserver.instances.push(this); } trigger(target: Element, isIntersecting: boolean) { this.callback( [{ isIntersecting, target } as IntersectionObserverEntry], this as unknown as IntersectionObserver ); } } describe('reveal action', () => { afterEach(() => { TestIntersectionObserver.instances = []; vi.unstubAllGlobals(); }); it('toggles visibility as the element enters and leaves the viewport', () => { vi.stubGlobal('IntersectionObserver', TestIntersectionObserver); vi.spyOn(window, 'matchMedia').mockReturnValue({ matches: false, media: '(prefers-reduced-motion: reduce)', onchange: null, addListener: vi.fn(), removeListener: vi.fn(), addEventListener: vi.fn(), removeEventListener: vi.fn(), dispatchEvent: vi.fn() } as MediaQueryList); const node = document.createElement('div'); document.body.appendChild(node); vi.spyOn(node, 'getBoundingClientRect').mockReturnValue({ width: 100, height: 100, top: window.innerHeight + 100, right: 100, bottom: window.innerHeight + 200, left: 0, x: 0, y: window.innerHeight + 100, toJSON() { return {}; } } as DOMRect); const action = reveal(node, { delay: 40, distance: 32 }); const observer = TestIntersectionObserver.instances[0]; expect(node.classList.contains('reveal-ready')).toBe(true); expect(node.classList.contains('reveal-visible')).toBe(false); expect(node.style.getPropertyValue('--reveal-delay')).toBe('40ms'); expect(node.style.getPropertyValue('--reveal-distance')).toBe('32px'); observer.trigger(node, true); expect(node.classList.contains('reveal-visible')).toBe(true); observer.trigger(node, false); expect(node.classList.contains('reveal-visible')).toBe(false); action.destroy(); expect(observer.disconnect).toHaveBeenCalledTimes(1); }); });