import { screen, fireEvent, waitFor } from '@testing-library/react'; import { renderWithProviders as render } from '../../../test/render'; import { describe, it, expect, vi, beforeEach } from 'vitest'; import { Sidebar } from './sidebar'; import React from 'react'; // Mock framer-motion vi.mock('framer-motion', () => ({ motion: { div: ({ children, ...props }: any) =>
{children}
, button: ({ children, ...props }: any) => , }, AnimatePresence: ({ children }: any) => <>{children}, })); describe('Sidebar', () => { const defaultProps = { expanded: true, onToggle: vi.fn(), user: { email: 'test@example.com' }, onLogout: vi.fn(), location: { pathname: '/' }, navigate: vi.fn(), routes: [ { path: '/', label: 'Home', icon: () =>
}, { path: '/dashboard', label: 'Dashboard', icon: () =>
}, ], }; beforeEach(() => { vi.clearAllMocks(); // Mock clientHeight for overflow detection Object.defineProperty(HTMLElement.prototype, 'clientHeight', { configurable: true, value: 1000, }); }); it('renders with local fallback state when no LayoutProvider is present', () => { render(); expect(screen.getByLabelText('Expandir menu')).toBeInTheDocument(); }); it('renders correctly when expanded', async () => { render(); // Sidebar also renders an aria-hidden MeasurementClone (off-screen copy of // the nav items used to measure real heights for overflow detection), so // queries must ignore it to avoid matching both copies of each label. await waitFor(() => { expect(screen.getByText('InĂ­cio', { ignore: '[aria-hidden="true"] *' })).toBeInTheDocument(); // Translated from Home expect(screen.getByText('Painel', { ignore: '[aria-hidden="true"] *' })).toBeInTheDocument(); // Translated from Dashboard }); }); it('calls onToggle when toggle button is clicked', () => { render(); const toggleButton = screen.getByLabelText('Recolher menu'); fireEvent.click(toggleButton); expect(defaultProps.onToggle).toHaveBeenCalled(); }); it('calls navigate when a menu item is clicked', async () => { render(); // Ignore the aria-hidden MeasurementClone's duplicate copy of the label. const dashboardLink = await screen.findByText('Painel', { ignore: '[aria-hidden="true"] *' }); fireEvent.click(dashboardLink); expect(defaultProps.navigate).toHaveBeenCalledWith('/dashboard'); }); it('renders correctly in assistant variant', () => { render( ); expect(screen.getByPlaceholderText('Buscar conversas')).toBeInTheDocument(); }); it('renders the Exit Assistant button in assistant variant (expanded) and calls onExitAssistant on click', () => { const onExitAssistant = vi.fn(); render(); fireEvent.click(screen.getByText('Sair do Assistente')); expect(onExitAssistant).toHaveBeenCalledTimes(1); }); it('does not render the Exit Assistant button in default variant, even if showExitAssistant is forced true', () => { render(); expect(screen.queryByText('Sair do Assistente')).not.toBeInTheDocument(); expect(screen.queryByLabelText('Sair do Assistente')).not.toBeInTheDocument(); }); it('lets consumers opt out of the Exit Assistant button via showExitAssistant={false}', () => { render(); expect(screen.queryByText('Sair do Assistente')).not.toBeInTheDocument(); expect(screen.queryByLabelText('Sair do Assistente')).not.toBeInTheDocument(); }); it('falls back to window.history.back() when onExitAssistant is not provided', () => { const backSpy = vi.spyOn(window.history, 'back').mockImplementation(() => {}); render(); fireEvent.click(screen.getByText('Sair do Assistente')); expect(backSpy).toHaveBeenCalledTimes(1); backSpy.mockRestore(); }); it('renders the Exit Assistant button as icon-only with a label when collapsed', () => { render(); expect(screen.getByLabelText('Sair do Assistente')).toBeInTheDocument(); expect(screen.getByLabelText('Expandir menu')).toBeInTheDocument(); }); it('regression: toggle button still calls onToggle and reflects aria-expanded once the Exit Assistant button is present', () => { const onToggle = vi.fn(); const onExitAssistant = vi.fn(); render( ); const toggleButton = screen.getByLabelText('Recolher menu'); expect(toggleButton).toHaveAttribute('aria-expanded', 'true'); fireEvent.click(toggleButton); expect(onToggle).toHaveBeenCalledTimes(1); expect(onExitAssistant).not.toHaveBeenCalled(); }); it('regression: Exit Assistant click does not trigger onToggle', () => { const onToggle = vi.fn(); render( ); fireEvent.click(screen.getByText('Sair do Assistente')); expect(onToggle).not.toHaveBeenCalled(); }); });