import React from 'react' import { renderWithProviders, userEvent, screen } from '@/__tests__/utils/test-helpers' import PortfolioDashboard from './portfolio-dashboard' jest.mock('@/components/ui/button', () => ({ Button: ({ children, ...props }: any) => , })) jest.mock('lucide-react', () => ({ Upload: () => , Rocket: () => , Plus: () => , Edit2: () => , Trash2: () => , Eye: () => , Globe: () => , Clock: () => , AlertCircle: () => , CheckCircle: () => , })) describe('Portfolio Dashboard Component', () => { const mockPortfolios = [ { id: 1, name: 'Personal Website', domain: 'myportfolio.com', status: 'active' as const, url: 'https://myportfolio.com', uploadedAt: '2025-10-15', lastUpdated: '2025-10-17', }, { id: 2, name: 'Design Showcase', domain: 'designs.myportfolio.com', status: 'inactive' as const, uploadedAt: '2025-10-10', lastUpdated: '2025-10-10', }, ] const mockHandlers = { onEdit: jest.fn(), onDelete: jest.fn(), onDeploy: jest.fn(), onUpload: jest.fn(), } beforeEach(() => { jest.clearAllMocks() }) describe('Header & Layout', () => { it('should render dashboard header', () => { renderWithProviders() expect(screen.getByText('Portfolio Management')).toBeInTheDocument() expect(screen.getByText(/manage and monitor your hosted portfolios/i)).toBeInTheDocument() }) it('should have grid and list view toggle buttons', () => { renderWithProviders() expect(screen.getByRole('button', { name: /grid view/i })).toBeInTheDocument() expect(screen.getByRole('button', { name: /list view/i })).toBeInTheDocument() }) }) describe('Statistics', () => { it('should display portfolio statistics', () => { renderWithProviders() expect(screen.getByText('Total Portfolios')).toBeInTheDocument() expect(screen.getByText('Active')).toBeInTheDocument() expect(screen.getByText('Inactive')).toBeInTheDocument() expect(screen.getByText('Failed')).toBeInTheDocument() }) it('should show correct portfolio counts', () => { renderWithProviders() const stats = screen.getAllByText(/\d/).filter((el) => el.textContent === '2' || el.textContent === '1') expect(stats.length).toBeGreaterThan(0) }) }) describe('Grid View', () => { it('should display portfolios in grid view by default', () => { renderWithProviders() mockPortfolios.forEach((portfolio) => { expect(screen.getByText(portfolio.name)).toBeInTheDocument() expect(screen.getByText(portfolio.domain)).toBeInTheDocument() }) }) it('should show portfolio status badges', () => { renderWithProviders() expect(screen.getByText('Active')).toBeInTheDocument() expect(screen.getByText('Inactive')).toBeInTheDocument() }) it('should display edit and delete buttons for each portfolio', () => { renderWithProviders() const editButtons = screen.getAllByRole('button', { name: /edit/i }) const trashButtons = screen.getAllByRole('button', { name: /trash/i }) expect(editButtons.length).toBeGreaterThanOrEqual(mockPortfolios.length) expect(trashButtons.length).toBeGreaterThanOrEqual(mockPortfolios.length) }) it('should show view button for active portfolios', () => { renderWithProviders() const viewButtons = screen.getAllByRole('button', { name: /view/i }) expect(viewButtons.length).toBeGreaterThan(0) }) }) describe('List View', () => { it('should switch to list view when button clicked', async () => { renderWithProviders() const listButton = screen.getByRole('button', { name: /list view/i }) await userEvent.click(listButton) mockPortfolios.forEach((portfolio) => { expect(screen.getByText(portfolio.name)).toBeInTheDocument() }) }) it('should display upload button in list view', async () => { renderWithProviders() const listButton = screen.getByRole('button', { name: /list view/i }) await userEvent.click(listButton) const uploadButtons = screen.getAllByRole('button') expect(uploadButtons.some((btn) => btn.querySelector('[data-testid="upload-icon"]'))).toBe(true) }) }) describe('Empty State', () => { it('should display empty state when no portfolios', () => { renderWithProviders() expect(screen.getByText('No portfolios yet')).toBeInTheDocument() expect(screen.getByText(/create your first portfolio to get started/i)).toBeInTheDocument() }) it('should show create portfolio button in empty state', () => { renderWithProviders() expect(screen.getByRole('button', { name: /create portfolio/i })).toBeInTheDocument() }) }) describe('User Interactions', () => { it('should call onEdit when edit button clicked', async () => { renderWithProviders() const editButtons = screen.getAllByRole('button', { name: /edit/i }) await userEvent.click(editButtons[0]) expect(mockHandlers.onEdit).toHaveBeenCalledWith(mockPortfolios[0].id) }) it('should call onDelete when delete button clicked', async () => { renderWithProviders() const trashButtons = screen.getAllByRole('button', { name: /trash/i }) await userEvent.click(trashButtons[0]) expect(mockHandlers.onDelete).toHaveBeenCalledWith(mockPortfolios[0].id) }) it('should call onUpload when upload button clicked in list view', async () => { renderWithProviders() const listButton = screen.getByRole('button', { name: /list view/i }) await userEvent.click(listButton) const uploadButtons = screen.getAllByRole('button') const uploadBtn = uploadButtons.find((btn) => btn.querySelector('[data-testid="upload-icon"]')) if (uploadBtn) { await userEvent.click(uploadBtn) expect(mockHandlers.onUpload).toHaveBeenCalled() } }) }) describe('Portfolio Details', () => { it('should display portfolio domain', () => { renderWithProviders() mockPortfolios.forEach((portfolio) => { expect(screen.getByText(portfolio.domain)).toBeInTheDocument() }) }) it('should display uploaded date', () => { renderWithProviders() expect(screen.getByText(/uploaded:/i)).toBeInTheDocument() }) it('should display last updated date', () => { renderWithProviders() expect(screen.getByText(/last updated:/i)).toBeInTheDocument() }) }) describe('View External Portfolio', () => { it('should open portfolio in new window when view clicked', async () => { const openSpy = jest.fn() window.open = openSpy renderWithProviders() const viewButtons = screen.getAllByRole('button', { name: /view/i }) if (viewButtons.length > 0) { await userEvent.click(viewButtons[0]) // Note: window.open may not be called in test environment depending on Jest config } }) it('should not show view button for inactive portfolios', () => { const inactivePortfolio = [ { id: 1, name: 'Inactive Site', domain: 'inactive.com', status: 'inactive' as const, uploadedAt: '2025-10-15', lastUpdated: '2025-10-15', }, ] renderWithProviders() expect(screen.queryByRole('button', { name: /view/i })).not.toBeInTheDocument() }) }) describe('Responsive Behavior', () => { it('should render responsive grid layout', () => { const { container } = renderWithProviders() const gridContainer = container.querySelector('.grid') expect(gridContainer).toBeInTheDocument() expect(gridContainer).toHaveClass('grid-cols-1', 'md:grid-cols-2', 'lg:grid-cols-3') }) it('should have responsive header layout', () => { const { container } = renderWithProviders() const headerDiv = container.querySelector('.flex.flex-col.md\\:flex-row') expect(headerDiv).toBeInTheDocument() }) }) describe('Status Indicators', () => { it('should use different styling for different statuses', () => { const statusPortfolios = [ { id: 1, name: 'Portfolio A', domain: 'active.com', status: 'active' as const, uploadedAt: '', lastUpdated: '' }, { id: 2, name: 'Portfolio B', domain: 'inactive.com', status: 'inactive' as const, uploadedAt: '', lastUpdated: '' }, { id: 3, name: 'Portfolio C', domain: 'failed.com', status: 'failed' as const, uploadedAt: '', lastUpdated: '' }, ] renderWithProviders() // Check for status badge text and portfolio names expect(screen.getByText('Portfolio A')).toBeInTheDocument() expect(screen.getByText('Portfolio B')).toBeInTheDocument() expect(screen.getByText('Portfolio C')).toBeInTheDocument() // Status should be shown const activeText = screen.getAllByText('Active') expect(activeText.length).toBeGreaterThan(0) }) }) })