import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
import { render, screen, fireEvent, waitFor, cleanup } from '@testing-library/react'
import Home from './page'
global.fetch = vi.fn()
const mockFetch = vi.mocked(global.fetch)
describe('Home Page', () => {
beforeEach(() => {
vi.clearAllMocks()
})
afterEach(() => {
cleanup()
})
it('renders the main heading', () => {
render()
expect(screen.getByText('Downlink')).toBeInTheDocument()
})
it('renders the URL input', () => {
render()
const inputs = screen.getAllByPlaceholderText('Paste YouTube URL here...')
expect(inputs.length).toBeGreaterThanOrEqual(1)
})
it('renders the Fetch button', () => {
render()
const buttons = screen.getAllByRole('button', { name: /fetch/i })
expect(buttons.length).toBeGreaterThanOrEqual(1)
})
it('shows error for empty URL on fetch', async () => {
render()
const fetchButtons = screen.getAllByRole('button', { name: /fetch/i })
fireEvent.click(fetchButtons[0])
await waitFor(() => {
expect(screen.getByText('Please enter a YouTube URL')).toBeInTheDocument()
})
})
it('shows error for invalid YouTube URL', async () => {
render()
const inputs = screen.getAllByPlaceholderText('Paste YouTube URL here...')
fireEvent.change(inputs[0], { target: { value: 'https://vimeo.com/123' } })
const fetchButtons = screen.getAllByRole('button', { name: /fetch/i })
fireEvent.click(fetchButtons[0])
await waitFor(() => {
expect(screen.getByText('Please enter a valid YouTube URL')).toBeInTheDocument()
})
})
it('fetches video info for valid URL', async () => {
const mockVideoInfo = {
title: 'Test Video',
duration: 180,
thumbnail: 'https://example.com/thumb.jpg',
}
mockFetch.mockResolvedValueOnce({
json: () => Promise.resolve({ success: true, videoInfo: mockVideoInfo }),
} as Response)
render()
const inputs = screen.getAllByPlaceholderText('Paste YouTube URL here...')
fireEvent.change(inputs[0], { target: { value: 'https://youtube.com/watch?v=dQw4w9WgXcQ' } })
const fetchButtons = screen.getAllByRole('button', { name: /fetch/i })
fireEvent.click(fetchButtons[0])
await waitFor(() => {
expect(screen.getByText('Test Video')).toBeInTheDocument()
})
expect(mockFetch).toHaveBeenCalledWith('/api/info', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ url: 'https://youtube.com/watch?v=dQw4w9WgXcQ' }),
})
})
it('displays video duration after fetch', async () => {
const mockVideoInfo = {
title: 'Test Video',
duration: 125,
thumbnail: 'https://example.com/thumb.jpg',
}
mockFetch.mockResolvedValueOnce({
json: () => Promise.resolve({ success: true, videoInfo: mockVideoInfo }),
} as Response)
render()
const inputs = screen.getAllByPlaceholderText('Paste YouTube URL here...')
fireEvent.change(inputs[0], { target: { value: 'https://youtube.com/watch?v=dQw4w9WgXcQ' } })
const fetchButtons = screen.getAllByRole('button', { name: /fetch/i })
fireEvent.click(fetchButtons[0])
await waitFor(() => {
expect(screen.getByText('2:05')).toBeInTheDocument()
})
})
it('shows download options after fetching video info', async () => {
const mockVideoInfo = {
title: 'Test Video',
duration: 180,
thumbnail: 'https://example.com/thumb.jpg',
}
mockFetch.mockResolvedValueOnce({
json: () => Promise.resolve({ success: true, videoInfo: mockVideoInfo }),
} as Response)
render()
const inputs = screen.getAllByPlaceholderText('Paste YouTube URL here...')
fireEvent.change(inputs[0], { target: { value: 'https://youtube.com/watch?v=dQw4w9WgXcQ' } })
const fetchButtons = screen.getAllByRole('button', { name: /fetch/i })
fireEvent.click(fetchButtons[0])
await waitFor(() => {
expect(screen.getByText('Download Options')).toBeInTheDocument()
})
})
it('handles API error response', async () => {
mockFetch.mockResolvedValueOnce({
json: () => Promise.resolve({ success: false, error: 'Video not found' }),
} as Response)
render()
const inputs = screen.getAllByPlaceholderText('Paste YouTube URL here...')
fireEvent.change(inputs[0], { target: { value: 'https://youtube.com/watch?v=dQw4w9WgXcQ' } })
const fetchButtons = screen.getAllByRole('button', { name: /fetch/i })
fireEvent.click(fetchButtons[0])
await waitFor(() => {
expect(screen.getByText('Video not found')).toBeInTheDocument()
})
})
it('handles fetch network error', async () => {
mockFetch.mockRejectedValueOnce(new Error('Network error'))
render()
const inputs = screen.getAllByPlaceholderText('Paste YouTube URL here...')
fireEvent.change(inputs[0], { target: { value: 'https://youtube.com/watch?v=dQw4w9WgXcQ' } })
const fetchButtons = screen.getAllByRole('button', { name: /fetch/i })
fireEvent.click(fetchButtons[0])
await waitFor(() => {
expect(screen.getByText('Network error')).toBeInTheDocument()
})
})
it('allows switching between video and audio format types', async () => {
const mockVideoInfo = {
title: 'Test Video',
duration: 180,
thumbnail: 'https://example.com/thumb.jpg',
}
mockFetch.mockResolvedValueOnce({
json: () => Promise.resolve({ success: true, videoInfo: mockVideoInfo }),
} as Response)
render()
const inputs = screen.getAllByPlaceholderText('Paste YouTube URL here...')
fireEvent.change(inputs[0], { target: { value: 'https://youtube.com/watch?v=dQw4w9WgXcQ' } })
const fetchButtons = screen.getAllByRole('button', { name: /fetch/i })
fireEvent.click(fetchButtons[0])
await waitFor(() => {
expect(screen.getByText('Download Options')).toBeInTheDocument()
})
const audioButton = screen.getByRole('button', { name: /audio/i })
fireEvent.click(audioButton)
expect(screen.getByText('Audio Format')).toBeInTheDocument()
})
it('triggers download on button click', async () => {
const mockVideoInfo = {
title: 'Test Video',
duration: 180,
thumbnail: 'https://example.com/thumb.jpg',
}
mockFetch
.mockResolvedValueOnce({
json: () => Promise.resolve({ success: true, videoInfo: mockVideoInfo }),
} as Response)
.mockResolvedValueOnce({
json: () => Promise.resolve({
success: true,
downloadUrl: '/downloads/test.mp4',
filename: 'test.mp4',
}),
} as Response)
render()
const inputs = screen.getAllByPlaceholderText('Paste YouTube URL here...')
fireEvent.change(inputs[0], { target: { value: 'https://youtube.com/watch?v=dQw4w9WgXcQ' } })
const fetchButtons = screen.getAllByRole('button', { name: /fetch/i })
fireEvent.click(fetchButtons[0])
await waitFor(() => {
expect(screen.getByText('Download Options')).toBeInTheDocument()
})
const downloadButton = screen.getByRole('button', { name: /download mp4/i })
fireEvent.click(downloadButton)
await waitFor(() => {
expect(mockFetch).toHaveBeenCalledTimes(2)
})
expect(mockFetch).toHaveBeenLastCalledWith('/api/download', expect.objectContaining({
method: 'POST',
}))
})
it('shows success message after download completes', async () => {
const mockVideoInfo = {
title: 'Test Video',
duration: 180,
thumbnail: 'https://example.com/thumb.jpg',
}
mockFetch
.mockResolvedValueOnce({
json: () => Promise.resolve({ success: true, videoInfo: mockVideoInfo }),
} as Response)
.mockResolvedValueOnce({
json: () => Promise.resolve({
success: true,
downloadUrl: '/downloads/test.mp4',
filename: 'test.mp4',
}),
} as Response)
render()
const inputs = screen.getAllByPlaceholderText('Paste YouTube URL here...')
fireEvent.change(inputs[0], { target: { value: 'https://youtube.com/watch?v=dQw4w9WgXcQ' } })
const fetchButtons = screen.getAllByRole('button', { name: /fetch/i })
fireEvent.click(fetchButtons[0])
await waitFor(() => {
expect(screen.getByText('Download Options')).toBeInTheDocument()
})
fireEvent.click(screen.getByRole('button', { name: /download mp4/i }))
await waitFor(() => {
expect(screen.getByText('Ready to Download')).toBeInTheDocument()
})
})
})