import React from 'react'
import { renderWithProviders, userEvent, waitFor } from '@/__tests__/utils/test-helpers'
import { createMockPortfolio } from '@/__tests__/utils/test-helpers'
import { useAuth } from '@/hooks/use-auth'
import { usePortfolios } from '@/hooks/use-portfolios'
import DashboardPage from './page'
jest.mock('@/hooks/use-auth')
jest.mock('@/hooks/use-portfolios')
jest.mock('@/components/ui/button', () => ({
Button: ({ children, ...props }: any) => ,
}))
jest.mock('@/components/ui/input', () => ({
Input: (props: any) => ,
}))
jest.mock('@/components/ui/label', () => ({
Label: ({ children, ...props }: any) => ,
}))
jest.mock('@/components/ui/card', () => ({
Card: ({ children }: any) =>
{children}
,
CardHeader: ({ children }: any) => {children}
,
CardTitle: ({ children }: any) => {children}
,
CardDescription: ({ children }: any) => {children}
,
CardContent: ({ children }: any) => {children}
,
CardFooter: ({ children }: any) => {children}
,
}))
jest.mock('lucide-react', () => ({
Upload: () => ,
Rocket: () => ,
Plus: () => ,
LogOut: () => ,
}))
describe('DashboardPage', () => {
const mockLogout = jest.fn()
const mockUser = { id: 1, name: 'John Doe', email: 'john@example.com', created_at: '', updated_at: '' }
const mockPortfolios = [
createMockPortfolio({ id: 1, name: 'Portfolio 1', active: true, path: '/uploads/1.zip' }),
createMockPortfolio({ id: 2, name: 'Portfolio 2', active: true, path: null }),
createMockPortfolio({ id: 3, name: 'Portfolio 3', active: false, path: null }),
]
const mockCreatePortfolio = jest.fn()
const mockUploadPortfolio = jest.fn()
const mockDeployPortfolio = jest.fn()
beforeEach(() => {
jest.clearAllMocks()
;(useAuth as jest.Mock).mockReturnValue({
user: mockUser,
logout: mockLogout,
})
;(usePortfolios as jest.Mock).mockReturnValue({
portfolios: mockPortfolios,
isLoading: false,
error: null,
createPortfolio: mockCreatePortfolio,
uploadPortfolio: mockUploadPortfolio,
deployPortfolio: mockDeployPortfolio,
})
})
it('should render dashboard with header', () => {
const { getByText, getByRole } = renderWithProviders()
expect(getByText('Portfolio Dashboard')).toBeInTheDocument()
expect(getByText(`Welcome, ${mockUser.name}`)).toBeInTheDocument()
expect(getByRole('button', { name: /logout/i })).toBeInTheDocument()
})
it('should display portfolio statistics', () => {
const { getAllByText } = renderWithProviders()
expect(getAllByText('Total Portfolios').length).toBeGreaterThan(0)
const threes = getAllByText('3')
expect(threes.length).toBeGreaterThan(0)
const actives = getAllByText('Active')
expect(actives.length).toBeGreaterThan(0)
})
it.skip('should display list of portfolios', () => {
const { getByText } = renderWithProviders()
mockPortfolios.forEach((portfolio) => {
expect(getByText(portfolio.name)).toBeInTheDocument()
expect(getByText(portfolio.domain)).toBeInTheDocument()
})
})
it('should display portfolio status badges', () => {
const { getAllByText } = renderWithProviders()
// Status badges will appear, check that at least one exists
const uploadedBadges = getAllByText('Uploaded')
expect(uploadedBadges.length).toBeGreaterThan(0)
})
it('should show create portfolio form when button clicked', async () => {
const { getByRole, getByText, getByPlaceholderText } = renderWithProviders()
const newPortfolioButton = getByRole('button', { name: /new portfolio/i })
await userEvent.click(newPortfolioButton)
expect(getByText('Create New Portfolio')).toBeInTheDocument()
expect(getByPlaceholderText('My Portfolio')).toBeInTheDocument()
expect(getByPlaceholderText('myportfolio.com')).toBeInTheDocument()
})
it('should create portfolio with valid data', async () => {
mockCreatePortfolio.mockResolvedValueOnce({ id: 4, name: 'New Portfolio', domain: 'new.com' })
const { getByRole, getByPlaceholderText } = renderWithProviders()
const newPortfolioButton = getByRole('button', { name: /new portfolio/i })
await userEvent.click(newPortfolioButton)
const nameInput = getByPlaceholderText('My Portfolio') as HTMLInputElement
const domainInput = getByPlaceholderText('myportfolio.com') as HTMLInputElement
const createButton = getByRole('button', { name: /^Create$/ })
await userEvent.type(nameInput, 'New Portfolio')
await userEvent.type(domainInput, 'new.com')
await userEvent.click(createButton)
expect(mockCreatePortfolio).toHaveBeenCalledWith('New Portfolio', 'new.com')
})
it('should close create form after successful creation', async () => {
mockCreatePortfolio.mockResolvedValueOnce({ id: 4 })
const { getByRole, getByPlaceholderText, queryByText } = renderWithProviders()
const newPortfolioButton = getByRole('button', { name: /new portfolio/i })
await userEvent.click(newPortfolioButton)
const nameInput = getByPlaceholderText('My Portfolio') as HTMLInputElement
const domainInput = getByPlaceholderText('myportfolio.com') as HTMLInputElement
const createButton = getByRole('button', { name: /^Create$/ })
await userEvent.type(nameInput, 'New Portfolio')
await userEvent.type(domainInput, 'new.com')
await userEvent.click(createButton)
await waitFor(() => {
expect(queryByText('Create New Portfolio')).not.toBeInTheDocument()
})
})
it('should cancel create form', async () => {
const { getByRole, queryByText } = renderWithProviders()
const newPortfolioButton = getByRole('button', { name: /new portfolio/i })
await userEvent.click(newPortfolioButton)
expect(queryByText('Create New Portfolio')).toBeInTheDocument()
const cancelButton = getByRole('button', { name: /cancel/i })
await userEvent.click(cancelButton)
expect(queryByText('Create New Portfolio')).not.toBeInTheDocument()
})
it('should handle upload button for pending upload portfolio', async () => {
const { getByRole, queryAllByRole } = renderWithProviders()
const uploadButtons = queryAllByRole('button', { name: /upload zip/i })
expect(uploadButtons.length).toBeGreaterThan(0)
})
it('should handle deploy button for uploaded portfolio', async () => {
mockDeployPortfolio.mockResolvedValueOnce({ id: 1 })
const { getByRole } = renderWithProviders()
const deployButtons = getByRole('button', { name: /^Deploy$/ })
await userEvent.click(deployButtons)
expect(mockDeployPortfolio).toHaveBeenCalledWith(1)
})
it('should show loading state', () => {
;(usePortfolios as jest.Mock).mockReturnValue({
portfolios: [],
isLoading: true,
error: null,
createPortfolio: mockCreatePortfolio,
uploadPortfolio: mockUploadPortfolio,
deployPortfolio: mockDeployPortfolio,
})
const { getByText } = renderWithProviders()
expect(getByText('Loading portfolios...')).toBeInTheDocument()
})
it('should show error message', () => {
const errorMessage = 'Failed to load portfolios'
;(usePortfolios as jest.Mock).mockReturnValue({
portfolios: [],
isLoading: false,
error: errorMessage,
createPortfolio: mockCreatePortfolio,
uploadPortfolio: mockUploadPortfolio,
deployPortfolio: mockDeployPortfolio,
})
const { getByText } = renderWithProviders()
expect(getByText(errorMessage)).toBeInTheDocument()
})
it('should show empty state when no portfolios', () => {
;(usePortfolios as jest.Mock).mockReturnValue({
portfolios: [],
isLoading: false,
error: null,
createPortfolio: mockCreatePortfolio,
uploadPortfolio: mockUploadPortfolio,
deployPortfolio: mockDeployPortfolio,
})
const { getByText } = renderWithProviders()
expect(getByText('No portfolios yet. Create your first one!')).toBeInTheDocument()
})
it('should call logout when logout button clicked', async () => {
const { getByRole } = renderWithProviders()
const logoutButton = getByRole('button', { name: /logout/i })
await userEvent.click(logoutButton)
expect(mockLogout).toHaveBeenCalled()
})
it('should display portfolio creation date', () => {
const { getAllByText } = renderWithProviders()
// Just check that creation dates are displayed
const dateTexts = getAllByText(/Created:/)
expect(dateTexts.length).toBeGreaterThan(0)
})
it('should handle upload loading state', async () => {
const { getByRole, getByText } = renderWithProviders()
const uploadButtons = getByRole('button', { name: /upload zip/i })
// Simulate upload in progress
await userEvent.click(uploadButtons)
// After click, button text should show loading
// Note: actual file upload state management would need more complex mocking
})
it('should handle deploy loading state', async () => {
const { getByRole, getByText } = renderWithProviders()
const deployButton = getByRole('button', { name: /^Deploy$/ })
await userEvent.click(deployButton)
expect(mockDeployPortfolio).toHaveBeenCalled()
})
it('should render statistics with responsive grid', () => {
const { getAllByTestId } = renderWithProviders()
const statCards = getAllByTestId('card')
// Should have at least stat cards + portfolio cards
expect(statCards.length).toBeGreaterThanOrEqual(3)
})
})