// @vitest-environment jsdom import { describe, expect, it, vi } from 'vitest' import { render, act } from '@testing-library/react' import { useState } from 'react' import { OverlayOpenRegistryProvider, useReportOverlayOpen, } from '../overlay-open-registry' /** Stand-in for `ActionsMenuDropdown` — reports its open state, renders nothing. */ function Overlay({ open }: { open: boolean }) { useReportOverlayOpen(open) return null } describe('OverlayOpenRegistry', () => { it('fires only on 0 → 1 and 1 → 0, not per overlay', () => { const onOpenChange = vi.fn() function Harness({ a, b }: { a: boolean; b: boolean }) { return ( ) } const { rerender } = render() expect(onOpenChange).not.toHaveBeenCalled() rerender() expect(onOpenChange.mock.calls).toEqual([[true]]) // Second overlay opens while the first is still open — no new signal. rerender() expect(onOpenChange.mock.calls).toEqual([[true]]) // First closes, one still open — still no signal. rerender() expect(onOpenChange.mock.calls).toEqual([[true]]) rerender() expect(onOpenChange.mock.calls).toEqual([[true], [false]]) }) it('releases when an open overlay unmounts', () => { const onOpenChange = vi.fn() function Harness({ mounted }: { mounted: boolean }) { return ( {mounted ? : null} ) } const { rerender } = render() expect(onOpenChange.mock.calls).toEqual([[true]]) rerender() expect(onOpenChange.mock.calls).toEqual([[true], [false]]) }) it('is inert without a provider — no throw, no crash', () => { expect(() => render()).not.toThrow() }) it('a stable context value keeps overlays from re-rendering on count changes', () => { const renderSpy = vi.fn() function CountingOverlay({ open }: { open: boolean }) { renderSpy() useReportOverlayOpen(open) return null } let bump: (() => void) | undefined function Harness() { const [, setTick] = useState(0) bump = () => setTick((t) => t + 1) return ( ) } render() const before = renderSpy.mock.calls.length act(() => bump?.()) // One re-render from the parent's own state, not extra churn from context. expect(renderSpy.mock.calls.length).toBe(before + 1) }) })