import React from 'react' import { render, screen, fireEvent } from '@testing-library/react' import '@testing-library/jest-dom' import { Popover, PopoverTrigger, PopoverContent, PopoverAnchor, PopoverClose, PopoverSeparator, PopoverHeader, PopoverFooter, popoverContentVariants, } from '../popover' // Mock Radix UI Popover components jest.mock('@radix-ui/react-popover', () => { const mockForwardRef = (component: any) => { const forwardedComponent = (props: any) => component(props, null) forwardedComponent.displayName = component.displayName || component.name return forwardedComponent } return { Root: ({ children, ...props }: any) =>
{children}
, Trigger: mockForwardRef(({ children, ...props }: any, ref: any) => ( )), Content: mockForwardRef(({ children, className, onInteractOutside, ...props }: any, ref: any) => (
onInteractOutside?.(e)} {...props} > {children}
)), Anchor: mockForwardRef(({ children, ...props }: any, ref: any) => (
{children}
)), Close: mockForwardRef(({ children, ...props }: any, ref: any) => ( )), } }) describe('Popover Components', () => { describe('Popover Component', () => { it('renders correctly with default props', () => { render( Open Content ) // Popover Root doesn't render a DOM element, just manages state expect(screen.getByTestId('popover-trigger')).toBeInTheDocument() expect(screen.getByTestId('popover-content')).toBeInTheDocument() }) it('passes through HTML attributes', () => { render( Open ) const popover = screen.getByTestId('popover-root') expect(popover).toHaveAttribute('data-custom', 'test') expect(popover).toHaveAttribute('aria-label', 'Custom popover') }) }) describe('PopoverTrigger Component', () => { it('renders correctly with default props', () => { render( Open Popover ) const trigger = screen.getByTestId('popover-trigger') expect(trigger).toBeInTheDocument() expect(trigger).toHaveTextContent('Open Popover') }) it('handles click events', () => { const onClick = jest.fn() render( Open ) const trigger = screen.getByTestId('popover-trigger') fireEvent.click(trigger) expect(onClick).toHaveBeenCalled() }) it('passes through HTML attributes', () => { render( Open ) const trigger = screen.getByTestId('popover-trigger') expect(trigger).toHaveAttribute('data-custom', 'test') expect(trigger).toHaveAttribute('aria-label', 'Open popover') }) }) describe('PopoverContent Component', () => { it('renders correctly with default props', () => { render( Popover content ) const content = screen.getByTestId('popover-content') expect(content).toBeInTheDocument() expect(content).toHaveTextContent('Popover content') }) it('applies custom className', () => { render( Content ) const content = screen.getByTestId('popover-content') expect(content).toHaveClass('custom-class') }) it('renders default variant correctly', () => { render( Content ) const content = screen.getByTestId('popover-content') expect(content).toBeInTheDocument() }) it('renders destructive variant correctly', () => { render( Content ) const content = screen.getByTestId('popover-content') expect(content).toBeInTheDocument() }) it('renders outline variant correctly', () => { render( Content ) const content = screen.getByTestId('popover-content') expect(content).toBeInTheDocument() }) it('renders subtle variant correctly', () => { render( Content ) const content = screen.getByTestId('popover-content') expect(content).toBeInTheDocument() }) it('renders sm size correctly', () => { render( Content ) const content = screen.getByTestId('popover-content') expect(content).toBeInTheDocument() }) it('renders default size correctly', () => { render( Content ) const content = screen.getByTestId('popover-content') expect(content).toBeInTheDocument() }) it('renders lg size correctly', () => { render( Content ) const content = screen.getByTestId('popover-content') expect(content).toBeInTheDocument() }) it('renders different radius options correctly', () => { const radiusOptions = ['none', 'sm', 'default', 'lg', 'full'] as const radiusOptions.forEach((radius) => { render( Content ) expect(screen.getByTestId(`content-${radius}`)).toBeInTheDocument() }) }) it('renders different shadow options correctly', () => { const shadowOptions = ['none', 'sm', 'default', 'md', 'lg', 'xl'] as const shadowOptions.forEach((shadow) => { render( Content ) expect(screen.getByTestId(`content-${shadow}`)).toBeInTheDocument() }) }) it('handles backdrop prop', () => { render( Content ) const content = screen.getByTestId('popover-content') expect(content).toBeInTheDocument() }) it('handles overlayBackdrop prop', () => { render( Content ) const content = screen.getByTestId('popover-content') expect(content).toBeInTheDocument() // Overlay backdrop creates a div with fixed positioning expect(document.querySelector('.fixed.inset-0')).toBeInTheDocument() }) it('handles closeOnInteractOutside prop', () => { const onInteractOutside = jest.fn() render( Content ) const content = screen.getByTestId('popover-content') fireEvent.click(content) // Should prevent default when closeOnInteractOutside is false expect(content).toBeInTheDocument() }) it('forwards ref correctly', () => { const ref = React.createRef() render( Content ) expect(ref.current).toBeInstanceOf(HTMLDivElement) }) it('maintains displayName', () => { expect(PopoverContent.displayName).toBeDefined() }) it('passes through HTML attributes', () => { render( Content ) const content = screen.getByTestId('popover-content') expect(content).toHaveAttribute('data-custom', 'test') expect(content).toHaveAttribute('aria-label', 'Popover content') }) }) describe('PopoverAnchor Component', () => { it('renders correctly with default props', () => { render( Anchor ) const anchor = screen.getByTestId('popover-anchor') expect(anchor).toBeInTheDocument() expect(anchor).toHaveTextContent('Anchor') }) it('passes through HTML attributes', () => { render( Anchor ) const anchor = screen.getByTestId('popover-anchor') expect(anchor).toHaveAttribute('data-custom', 'test') }) }) describe('PopoverClose Component', () => { it('renders correctly with default props', () => { render( Close ) const close = screen.getByTestId('popover-close') expect(close).toBeInTheDocument() expect(close).toHaveTextContent('Close') }) it('handles click events', () => { const onClick = jest.fn() render( Close ) const close = screen.getByTestId('popover-close') fireEvent.click(close) expect(onClick).toHaveBeenCalled() }) it('passes through HTML attributes', () => { render( Close ) const close = screen.getByTestId('popover-close') expect(close).toHaveAttribute('data-custom', 'test') expect(close).toHaveAttribute('aria-label', 'Close popover') }) }) describe('PopoverSeparator Component', () => { it('renders correctly with default props', () => { render() const separator = screen.getByTestId('popover-separator') expect(separator).toBeInTheDocument() expect(separator).toHaveClass('my-2', 'h-px', 'bg-border') }) it('applies custom className', () => { render() const separator = screen.getByTestId('separator') expect(separator).toHaveClass('custom-separator') expect(separator).toHaveClass('my-2', 'h-px', 'bg-border') }) it('maintains displayName', () => { expect(PopoverSeparator.displayName).toBe('PopoverSeparator') }) it('passes through HTML attributes', () => { render() const separator = screen.getByTestId('separator') expect(separator).toHaveAttribute('data-custom', 'test') }) }) describe('PopoverHeader Component', () => { it('renders correctly with default props', () => { render(Header) const header = screen.getByTestId('popover-header') expect(header).toBeInTheDocument() expect(header).toHaveTextContent('Header') expect(header).toHaveClass('-mx-4', '-mt-4', 'mb-3', 'px-4', 'pt-4', 'pb-3', 'border-b', 'border-border') }) it('applies custom className', () => { render(Header) const header = screen.getByTestId('header') expect(header).toHaveClass('custom-header') expect(header).toHaveClass('-mx-4', '-mt-4', 'mb-3') }) it('maintains displayName', () => { expect(PopoverHeader.displayName).toBe('PopoverHeader') }) it('passes through HTML attributes', () => { render(Header) const header = screen.getByTestId('header') expect(header).toHaveAttribute('data-custom', 'test') }) }) describe('PopoverFooter Component', () => { it('renders correctly with default props', () => { render(Footer) const footer = screen.getByTestId('popover-footer') expect(footer).toBeInTheDocument() expect(footer).toHaveTextContent('Footer') expect(footer).toHaveClass('-mx-4', '-mb-4', 'mt-3', 'px-4', 'pt-3', 'pb-4', 'border-t', 'border-border') }) it('applies custom className', () => { render(Footer) const footer = screen.getByTestId('footer') expect(footer).toHaveClass('custom-footer') expect(footer).toHaveClass('-mx-4', '-mb-4', 'mt-3') }) it('maintains displayName', () => { expect(PopoverFooter.displayName).toBe('PopoverFooter') }) it('passes through HTML attributes', () => { render(Footer) const footer = screen.getByTestId('footer') expect(footer).toHaveAttribute('data-custom', 'test') }) }) describe('Variant Functions', () => { it('popoverContentVariants function works correctly', () => { const variants = popoverContentVariants({ variant: 'destructive', size: 'lg', radius: 'full', shadow: 'xl' }) expect(typeof variants).toBe('string') expect(variants.length).toBeGreaterThan(0) }) }) describe('Complex Combinations', () => { it('renders complete popover with all components', () => { render( Open Anchor Header
Content
Footer Close
) expect(screen.getByTestId('popover-root')).toBeInTheDocument() expect(screen.getByTestId('popover-trigger')).toBeInTheDocument() expect(screen.getByTestId('popover-anchor')).toBeInTheDocument() expect(screen.getByTestId('popover-content')).toBeInTheDocument() expect(screen.getByTestId('popover-close')).toBeInTheDocument() expect(screen.getByText('Header')).toBeInTheDocument() expect(screen.getByText('Content')).toBeInTheDocument() expect(screen.getByText('Footer')).toBeInTheDocument() }) it('handles all content variants and sizes together', () => { render(
Destructive Small Outline Large
) expect(screen.getByText('Destructive Small')).toBeInTheDocument() expect(screen.getByText('Outline Large')).toBeInTheDocument() }) it('handles backdrop and overlay combinations', () => { render( Complex Content ) expect(screen.getByText('Complex Content')).toBeInTheDocument() expect(document.querySelector('.fixed.inset-0')).toBeInTheDocument() }) }) describe('Edge Cases', () => { it('handles empty popover', () => { render() expect(screen.getByTestId('popover-root')).toBeInTheDocument() }) it('handles popover without content', () => { render( Open ) expect(screen.getByTestId('popover-trigger')).toBeInTheDocument() }) it('handles null and undefined children', () => { render( {null} {undefined}
Valid content
) expect(screen.getByText('Valid content')).toBeInTheDocument() }) it('handles complex nested content', () => { render(

Complex Header

With multiple elements

  • Item 1
  • Item 2
Cancel
) expect(screen.getByText('Complex Header')).toBeInTheDocument() expect(screen.getByText('With multiple elements')).toBeInTheDocument() expect(screen.getByText('Item 1')).toBeInTheDocument() expect(screen.getByText('Item 2')).toBeInTheDocument() expect(screen.getByText('Action 1')).toBeInTheDocument() expect(screen.getByText('Cancel')).toBeInTheDocument() }) it('handles multiple popovers on same page', () => { render(
First Trigger First Content Second Trigger Second Content
) expect(screen.getByText('First Trigger')).toBeInTheDocument() expect(screen.getByText('First Content')).toBeInTheDocument() expect(screen.getByText('Second Trigger')).toBeInTheDocument() expect(screen.getByText('Second Content')).toBeInTheDocument() }) }) })