import React from 'react'; import { render, screen, waitFor } from '@testing-library/react'; import { describe, it, expect, vi, beforeEach } from 'vitest'; import { Popover } from './popover'; import type {} from '../../types/popover'; // Mock showPopover and hidePopover methods beforeEach(() => { HTMLElement.prototype.showPopover = vi.fn(function (this: HTMLElement) { this.setAttribute('data-popover-open', 'true'); }) as unknown as () => void; HTMLElement.prototype.hidePopover = vi.fn(function (this: HTMLElement) { this.removeAttribute('data-popover-open'); }) as unknown as () => void; HTMLElement.prototype.togglePopover = vi.fn(function (this: HTMLElement) { if (this.hasAttribute('data-popover-open')) { this.removeAttribute('data-popover-open'); } else { this.setAttribute('data-popover-open', 'true'); } }) as unknown as (force?: boolean) => void; }); describe('Popover', () => { it('renders trigger button with default label', () => { render(

Content

); const trigger = screen.getByRole('button', { name: 'Open Menu' }); expect(trigger).toBeInTheDocument(); expect(trigger).toHaveAttribute('popovertarget', 'test-popover'); }); it('renders popover content with correct attributes', () => { render(

Popover content

); const popover = screen.getByText('Popover content').closest('[popover]'); expect(popover).toBeInTheDocument(); expect(popover).toHaveAttribute('popover', 'auto'); expect(popover).toHaveAttribute('data-placement', 'bottom'); }); it('uses custom trigger element', () => { render( Custom Trigger}>

Content

); const trigger = screen.getByRole('button', { name: 'Custom Trigger' }); expect(trigger).toBeInTheDocument(); expect(trigger).toHaveAttribute('popovertarget', 'test-popover'); }); it('shows close button in manual mode by default', () => { render(

Content

); const closeButton = screen.getByRole('button', { name: 'Close' }); expect(closeButton).toBeInTheDocument(); expect(closeButton).toHaveAttribute('popovertargetaction', 'hide'); }); it('hides close button in auto mode by default', () => { render(

Content

); const closeButton = screen.queryByRole('button', { name: 'Close' }); expect(closeButton).not.toBeInTheDocument(); }); it('respects showCloseButton prop override', () => { render(

Content

); const closeButton = screen.getByRole('button', { name: 'Close' }); expect(closeButton).toBeInTheDocument(); }); it('shows arrow by default', () => { const { container } = render(

Content

); const arrow = container.querySelector('.fpkit-popover-arrow'); expect(arrow).toBeInTheDocument(); }); it('hides arrow when showArrow is false', () => { const { container } = render(

Content

); const arrow = container.querySelector('.fpkit-popover-arrow'); expect(arrow).not.toBeInTheDocument(); }); it('applies custom className', () => { const { container } = render(

Content

); const popover = container.querySelector('.fpkit-popover.custom-class'); expect(popover).toBeInTheDocument(); }); it('applies inline styles', () => { const customStyles = { '--popover-bg': '#000000' } as React.CSSProperties; const { container } = render(

Content

); const popover = container.querySelector('.fpkit-popover') as HTMLElement; expect(popover.style.getPropertyValue('--popover-bg')).toBe('#000000'); }); it('calls onToggle callback when popover state changes', async () => { const handleToggle = vi.fn(); const { container } = render(

Content

); const popover = container.querySelector('[popover]') as HTMLElement; // Simulate toggle event - open const toggleEventOpen = Object.assign(new Event('toggle'), { newState: 'open' as const, oldState: 'closed' as const, }) as ToggleEvent; popover.dispatchEvent(toggleEventOpen); await waitFor(() => { expect(handleToggle).toHaveBeenCalledWith(true); }); // Simulate close const toggleEventClose = Object.assign(new Event('toggle'), { newState: 'closed' as const, oldState: 'open' as const, }) as ToggleEvent; popover.dispatchEvent(toggleEventClose); await waitFor(() => { expect(handleToggle).toHaveBeenCalledWith(false); }); }); it('handles controlled state with isOpen prop', async () => { const { rerender, container } = render(

Content

); const popover = container.querySelector('[popover]') as HTMLElement; // Initially closed expect(popover.hasAttribute('data-popover-open')).toBe(false); // Open popover rerender(

Content

); await waitFor(() => { expect(HTMLElement.prototype.showPopover).toHaveBeenCalled(); }); }); it('generates unique ID when not provided', () => { const { container } = render(

Content

); const popover = container.querySelector('[popover]'); const trigger = screen.getByRole('button'); expect(popover).toHaveAttribute('id'); expect(trigger).toHaveAttribute('popovertarget'); const popoverId = popover?.getAttribute('id'); const triggerId = trigger.getAttribute('popovertarget'); expect(popoverId).toBe(triggerId); }); it('uses provided ID', () => { render(

Content

); const trigger = screen.getByRole('button'); expect(trigger).toHaveAttribute('popovertarget', 'custom-id'); }); it('custom close button label', () => { render(

Content

); const closeButton = screen.getByRole('button', { name: 'Dismiss' }); expect(closeButton).toBeInTheDocument(); }); it('renders arrow with correct placement attribute', () => { const { container } = render(

Content

); const arrow = container.querySelector('.fpkit-popover-arrow'); expect(arrow).toHaveAttribute('data-placement', 'top'); }); });