import { afterEach, describe, it, expect } from 'vitest'; import { cleanup, render } from 'vitest-browser-svelte'; import { page } from 'vitest/browser'; import { createRawSnippet } from 'svelte'; import EntityNavSection from './EntityNavSection.svelte'; afterEach(cleanup); const testIcon = createRawSnippet(() => ({ render: () => ``, setup: () => {} })); const baseProps = { href: '/admin/users', label: 'Benutzer', icon: testIcon }; describe('EntityNavSection — sidebar variant (default)', () => { it('tablet button has border-brand-mint class when isActive=true', async () => { render(EntityNavSection, { ...baseProps, isActive: true }); const button = document.querySelector('button[data-flyout-trigger]')!; expect(button.className).toContain('border-brand-mint'); }); it('tablet button has border-transparent class when isActive=false', async () => { render(EntityNavSection, { ...baseProps, isActive: false }); const button = document.querySelector('button[data-flyout-trigger]')!; expect(button.className).toContain('border-transparent'); }); it('renders count span when count is provided', async () => { render(EntityNavSection, { ...baseProps, isActive: false, count: 42 }); // Sidebar renders two elements (tablet button + desktop link), each with a count span const countSpans = document.querySelectorAll('span'); const countTexts = Array.from(countSpans).filter((s) => s.textContent?.trim() === '42'); expect(countTexts.length).toBeGreaterThanOrEqual(1); }); it('does not render count span when count is undefined', async () => { render(EntityNavSection, { ...baseProps, isActive: false }); // No numeric count element — the label text is present but no count span const spans = document.querySelectorAll('button[data-flyout-trigger] span'); expect(spans.length).toBe(0); }); it('desktop link has hidden and lg:flex classes', async () => { render(EntityNavSection, { ...baseProps, isActive: false }); const link = document.querySelector('a[href="/admin/users"]')!; expect(link.className).toContain('hidden'); expect(link.className).toContain('lg:flex'); }); it('desktop link has aria-current=page when isActive=true', async () => { render(EntityNavSection, { ...baseProps, isActive: true }); await expect .element(page.getByRole('link', { name: 'Benutzer' })) .toHaveAttribute('aria-current', 'page'); }); it('desktop link does not have aria-current when isActive=false', async () => { render(EntityNavSection, { ...baseProps, isActive: false }); await expect .element(page.getByRole('link', { name: 'Benutzer' })) .not.toHaveAttribute('aria-current'); }); it('renders the icon in the tablet button', async () => { render(EntityNavSection, { ...baseProps, isActive: false }); const button = document.querySelector('button[data-flyout-trigger]')!; expect(button.querySelector('svg')).not.toBeNull(); }); it('renders count in desktop link when count is provided', async () => { render(EntityNavSection, { ...baseProps, isActive: false, count: 7 }); const link = document.querySelector('a[href="/admin/users"]')!; expect(link.textContent).toContain('7'); }); }); describe('EntityNavSection — topBorder prop', () => { it('tablet button has border-l-transparent (not border-transparent) when topBorder=true and inactive', async () => { render(EntityNavSection, { ...baseProps, isActive: false, topBorder: true }); const button = document.querySelector('button[data-flyout-trigger]')!; expect(button.className).toContain('border-l-transparent'); expect(button.className).not.toContain('border-transparent hover:bg-white/5'); }); it('tablet button still has border-brand-mint when topBorder=true and isActive=true', async () => { render(EntityNavSection, { ...baseProps, isActive: true, topBorder: true }); const button = document.querySelector('button[data-flyout-trigger]')!; expect(button.className).toContain('border-brand-mint'); }); }); describe('EntityNavSection — flyout variant', () => { it('renders a single anchor element (no button) in flyout variant', async () => { render(EntityNavSection, { ...baseProps, isActive: false, variant: 'flyout' }); expect(document.querySelector('button[data-flyout-trigger]')).toBeNull(); expect(document.querySelector('a[href="/admin/users"]')).not.toBeNull(); }); it('flyout link has border-brand-mint class when isActive=true', async () => { render(EntityNavSection, { ...baseProps, isActive: true, variant: 'flyout' }); const link = document.querySelector('a[href="/admin/users"]')!; expect(link.className).toContain('border-brand-mint'); }); it('flyout link has border-transparent class when isActive=false', async () => { render(EntityNavSection, { ...baseProps, isActive: false, variant: 'flyout' }); const link = document.querySelector('a[href="/admin/users"]')!; expect(link.className).toContain('border-transparent'); }); it('flyout link shows count when count=42', async () => { render(EntityNavSection, { ...baseProps, isActive: false, variant: 'flyout', count: 42 }); await expect.element(page.getByText('42')).toBeInTheDocument(); }); it('flyout link has aria-current=page when isActive=true', async () => { render(EntityNavSection, { ...baseProps, isActive: true, variant: 'flyout' }); const link = document.querySelector('a[href="/admin/users"]')!; expect(link.getAttribute('aria-current')).toBe('page'); }); it('flyout link calls onFlyoutClick when clicked', async () => { let called = false; render(EntityNavSection, { ...baseProps, isActive: false, variant: 'flyout', onFlyoutClick: () => { called = true; } }); document.querySelector('a[href="/admin/users"]')!.click(); expect(called).toBe(true); }); });