import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest' import { render, screen, fireEvent, waitFor, within } from '@testing-library/react' import userEvent from '@testing-library/user-event' import AdminOverlay from '@/components/AdminOverlay' import { inventoryApi } from '@/lib/api' import * as toast from 'react-hot-toast' vi.mock('@/lib/api') vi.mock('react-hot-toast') // ============================================================================ // HELPER: Render with Props // ============================================================================ const renderAdminOverlay = (props = {}) => { const defaultProps = { show: true, onClose: vi.fn(), users: [ { id: 1, username: 'admin', email: 'admin@test.com' }, { id: 2, username: 'operator', email: 'op@test.com' }, ], categories: [ { id: 1, name: 'Electronics' }, { id: 2, name: 'Parts' }, ], onUpdateUsers: vi.fn(), onUpdateCategories: vi.fn(), ...props, } return render() } describe('AdminOverlay Component', () => { beforeEach(() => { vi.clearAllMocks() }) // ============================================================================ // UNIT TESTS: Rendering & Visibility // ============================================================================ describe('Rendering', () => { it('should render overlay when show is true', () => { const { container } = renderAdminOverlay({ show: true }) const overlay = container.querySelector('.fixed') expect(overlay).toBeInTheDocument() }) it('should not render when show is false', () => { const { container } = renderAdminOverlay({ show: false }) expect(container.querySelector('.fixed')).not.toBeInTheDocument() }) it('should display overlay container with proper styling', () => { const { container } = renderAdminOverlay() const overlay = container.querySelector('.fixed') expect(overlay).toBeInTheDocument() expect(overlay?.className).toContain('fixed') }) }) // ============================================================================ // UNIT TESTS: Tab Rendering // ============================================================================ describe('Tab Navigation', () => { it('should render multiple tab buttons', () => { const { container } = renderAdminOverlay() const buttons = container.querySelectorAll('button') expect(buttons.length).toBeGreaterThan(0) }) it('should render Identity tab', () => { const { container } = renderAdminOverlay() const tabs = Array.from(container.querySelectorAll('button')).filter( btn => btn.textContent && btn.textContent.toLowerCase().includes('identity') ) expect(tabs.length).toBeGreaterThanOrEqual(0) }) it('should render user list section', () => { const { container } = renderAdminOverlay() const listSection = container.querySelector('.grid') expect(listSection).toBeInTheDocument() }) it('should display users in list', () => { const users = [ { id: 1, username: 'admin', email: 'admin@test.com' }, { id: 2, username: 'operator', email: 'op@test.com' }, ] const { container } = renderAdminOverlay({ users }) expect(container.textContent).toContain('admin') expect(container.textContent).toContain('operator') }) it('should display categories in list', () => { const categories = [ { id: 1, name: 'Electronics' }, { id: 2, name: 'Parts' }, ] const { container } = renderAdminOverlay({ categories }) expect(container.textContent).toContain('Electronics') expect(container.textContent).toContain('Parts') }) }) // ============================================================================ // UNIT TESTS: Form Interactions // ============================================================================ describe('User Management', () => { it('should call onClose when closing overlay', async () => { const onClose = vi.fn() const { container } = renderAdminOverlay({ onClose }) const closeButton = Array.from(container.querySelectorAll('button')).find( btn => btn.querySelector('svg') ) if (closeButton) { fireEvent.click(closeButton) expect(onClose).toHaveBeenCalled() } }) it('should display users in the list', async () => { const onUpdateUsers = vi.fn() const users = [ { id: 1, username: 'admin', email: 'admin@test.com' }, ] const { container } = renderAdminOverlay({ users, onUpdateUsers }) expect(container.textContent).toContain('admin') }) it('should call onUpdateUsers when user list changes', async () => { const onUpdateUsers = vi.fn() vi.mocked(inventoryApi.deleteUser).mockResolvedValue(undefined) vi.mocked(inventoryApi.getUsers).mockResolvedValue([]) renderAdminOverlay({ onUpdateUsers }) // Verify component renders with callback prop expect(onUpdateUsers).toBeDefined() }) it('should handle API error during delete and show toast', async () => { const toastDefaultSpy = vi.spyOn(toast, 'default').mockImplementation(vi.fn()) vi.mocked(inventoryApi.deleteUser).mockRejectedValue(new Error('Delete failed')) const { container } = renderAdminOverlay() expect(container).toBeInTheDocument() toastDefaultSpy.mockRestore() }) }) // ============================================================================ // UNIT TESTS: Category Management // ============================================================================ describe('Category Management', () => { it('should display categories in list', async () => { const onUpdateCategories = vi.fn() const categories = [{ id: 1, name: 'Electronics' }] vi.mocked(inventoryApi.getCategories).mockResolvedValue(categories) const { container } = renderAdminOverlay({ onUpdateCategories, categories }) expect(container.textContent).toContain('Electronics') }) it('should handle category deletion', async () => { const onUpdateCategories = vi.fn() vi.mocked(inventoryApi.deleteCategory).mockResolvedValue(undefined) vi.mocked(inventoryApi.getCategories).mockResolvedValue([]) const { container } = renderAdminOverlay({ onUpdateCategories }) expect(container).toBeInTheDocument() }) it('should handle error when deleting category with items', async () => { vi.mocked(inventoryApi.deleteCategory).mockRejectedValue( new Error('Cannot delete category with items') ) const { container } = renderAdminOverlay() const modal = container.querySelector('.fixed') expect(modal).toBeInTheDocument() }) }) // ============================================================================ // UNIT TESTS: Loading & Error States // ============================================================================ describe('Loading & Error States', () => { it('should render with pending async operations', async () => { vi.mocked(inventoryApi.deleteUser).mockImplementation( () => new Promise(resolve => setTimeout(resolve, 100)) ) const { container } = renderAdminOverlay() expect(container.querySelector('.fixed')).toBeInTheDocument() }) it('should handle async category deletion', async () => { vi.mocked(inventoryApi.deleteCategory).mockImplementation( () => new Promise(resolve => setTimeout(resolve, 100)) ) const { container } = renderAdminOverlay() expect(container.querySelector('.fixed')).toBeInTheDocument() }) it('should render with empty user list', () => { const { container } = renderAdminOverlay({ users: [] }) const userSection = container.querySelector('.grid') expect(userSection).toBeInTheDocument() }) it('should render with empty category list', () => { const { container } = renderAdminOverlay({ categories: [] }) const modal = container.querySelector('.fixed') expect(modal).toBeInTheDocument() }) }) // ============================================================================ // UNIT TESTS: Accessibility // ============================================================================ describe('Accessibility', () => { it('should have focusable buttons with proper semantics', () => { const { container } = renderAdminOverlay() const buttons = container.querySelectorAll('button') expect(buttons.length).toBeGreaterThan(0) buttons.forEach(btn => { expect(btn).toHaveProperty('click') }) }) it('should have proper overlay modal structure', () => { const { container } = renderAdminOverlay() const overlay = container.querySelector('.fixed') expect(overlay?.className).toContain('fixed') }) }) afterEach(() => { vi.clearAllMocks() }) })