/** * Tests for PopoverManager */ import { PopoverManager } from '../../resources/js/modules/livecomponent/PopoverManager.js'; describe('PopoverManager', () => { let manager; let anchor; beforeEach(() => { // Create anchor element for testing anchor = document.createElement('button'); anchor.id = 'test-anchor'; anchor.textContent = 'Anchor'; document.body.appendChild(anchor); manager = new PopoverManager(); }); afterEach(() => { if (manager) { manager.destroy(); } if (anchor && anchor.parentNode) { anchor.parentNode.removeChild(anchor); } // Clean up any remaining popovers document.querySelectorAll('.livecomponent-popover').forEach(el => el.remove()); }); describe('show', () => { it('should create and show popover', () => { const popover = manager.show('test-popover', { anchorId: 'test-anchor', content: 'Test content', position: 'top' }); expect(popover).toBeDefined(); expect(popover.element).toBeDefined(); const popoverElement = document.querySelector('.livecomponent-popover'); expect(popoverElement).toBeTruthy(); }); it('should return null when anchorId is missing', () => { const popover = manager.show('test-popover', { content: 'Test content' }); expect(popover).toBeNull(); }); it('should return null when anchor element not found', () => { const popover = manager.show('test-popover', { anchorId: 'non-existent', content: 'Test content' }); expect(popover).toBeNull(); }); it('should position popover relative to anchor', () => { anchor.style.position = 'absolute'; anchor.style.top = '100px'; anchor.style.left = '200px'; anchor.style.width = '100px'; anchor.style.height = '50px'; const popover = manager.show('test-popover', { anchorId: 'test-anchor', content: 'Test', position: 'top', offset: 10 }); expect(popover).toBeDefined(); const popoverElement = popover.element; // Check that popover is positioned expect(popoverElement.style.position).toBe('fixed'); expect(popoverElement.style.top).toBeTruthy(); expect(popoverElement.style.left).toBeTruthy(); }); it('should include title when provided', () => { manager.show('test-popover', { anchorId: 'test-anchor', title: 'Test Title', content: 'Test content' }); const titleElement = document.querySelector('.popover-title'); expect(titleElement).toBeTruthy(); expect(titleElement.textContent).toBe('Test Title'); }); it('should include arrow when showArrow is true', () => { manager.show('test-popover', { anchorId: 'test-anchor', showArrow: true, content: 'Test' }); const arrow = document.querySelector('.popover-arrow'); expect(arrow).toBeTruthy(); }); it('should not include arrow when showArrow is false', () => { manager.show('test-popover', { anchorId: 'test-anchor', showArrow: false, content: 'Test' }); const arrow = document.querySelector('.popover-arrow'); expect(arrow).toBeFalsy(); }); }); describe('hide', () => { it('should hide popover', () => { const popover = manager.show('test-popover', { anchorId: 'test-anchor', content: 'Test' }); expect(manager.popovers.has('test-popover')).toBe(true); manager.hide('test-popover'); expect(manager.popovers.has('test-popover')).toBe(false); }); it('should handle hiding non-existent popover gracefully', () => { expect(() => { manager.hide('non-existent'); }).not.toThrow(); }); }); describe('position calculation', () => { beforeEach(() => { anchor.style.position = 'absolute'; anchor.style.top = '100px'; anchor.style.left = '200px'; anchor.style.width = '100px'; anchor.style.height = '50px'; }); it('should position popover on top', () => { const popover = manager.show('test-popover', { anchorId: 'test-anchor', position: 'top', offset: 10 }); const rect = popover.element.getBoundingClientRect(); const anchorRect = anchor.getBoundingClientRect(); expect(rect.bottom).toBeLessThan(anchorRect.top); }); it('should position popover on bottom', () => { const popover = manager.show('test-popover', { anchorId: 'test-anchor', position: 'bottom', offset: 10 }); const rect = popover.element.getBoundingClientRect(); const anchorRect = anchor.getBoundingClientRect(); expect(rect.top).toBeGreaterThan(anchorRect.bottom); }); it('should position popover on left', () => { const popover = manager.show('test-popover', { anchorId: 'test-anchor', position: 'left', offset: 10 }); const rect = popover.element.getBoundingClientRect(); const anchorRect = anchor.getBoundingClientRect(); expect(rect.right).toBeLessThan(anchorRect.left); }); it('should position popover on right', () => { const popover = manager.show('test-popover', { anchorId: 'test-anchor', position: 'right', offset: 10 }); const rect = popover.element.getBoundingClientRect(); const anchorRect = anchor.getBoundingClientRect(); expect(rect.left).toBeGreaterThan(anchorRect.right); }); }); describe('auto positioning', () => { it('should choose best position when position is auto', () => { anchor.style.position = 'absolute'; anchor.style.top = '50px'; anchor.style.left = '50px'; const popover = manager.show('test-popover', { anchorId: 'test-anchor', position: 'auto', offset: 10 }); expect(popover).toBeDefined(); // Should position successfully expect(popover.element.style.position).toBe('fixed'); }); }); describe('viewport boundary detection', () => { it('should keep popover within viewport', () => { // Position anchor near top-left corner anchor.style.position = 'absolute'; anchor.style.top = '10px'; anchor.style.left = '10px'; const popover = manager.show('test-popover', { anchorId: 'test-anchor', position: 'top', offset: 100 // Large offset that would push outside viewport }); const rect = popover.element.getBoundingClientRect(); expect(rect.top).toBeGreaterThanOrEqual(0); expect(rect.left).toBeGreaterThanOrEqual(0); }); }); describe('destroy', () => { it('should cleanup all popovers', () => { manager.show('popover-1', { anchorId: 'test-anchor', content: 'Test 1' }); manager.show('popover-2', { anchorId: 'test-anchor', content: 'Test 2' }); expect(manager.popovers.size).toBeGreaterThan(0); manager.destroy(); expect(manager.popovers.size).toBe(0); }); }); describe('Popover API detection', () => { it('should detect Popover API support', () => { // This test checks if the manager correctly detects API support // Actual behavior depends on browser environment expect(manager.usePopoverAPI).toBeDefined(); expect(typeof manager.usePopoverAPI).toBe('boolean'); }); }); });