go-first/web/src/__tests__/App.test.tsx
2026-10-07 15:21:47 +02:00

129 lines
No EOL
4.7 KiB
TypeScript

import { render, screen, waitFor, act } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { BrowserRouter } from 'react-router-dom'
import { describe, it, expect, beforeEach } from 'vitest'
import App from '../App'
import { mockFetchResponse, resetFetchMock, getFetchCalls } from './api-mock'
const renderApp = () => {
return render(
<BrowserRouter>
<App />
</BrowserRouter>
)
}
const mockSkills = [
{ id: '1', name: 'Go', category: 'Backend', proficiency: 5, icon: 'go', order: 1, created_at: '', updated_at: '' },
{ id: '2', name: 'React', category: 'Frontend', proficiency: 4, icon: 'react', order: 2, created_at: '', updated_at: '' },
]
const mockProjects = [
{ id: '1', title: 'Test Project', description: 'Desc', short_desc: 'Short', tech_stack: ['Go', 'React'], image_url: '', demo_url: '', repo_url: '', category: 'Full Stack', featured: true, order: 1, created_at: '', updated_at: '' },
]
const mockExperience = [
{ id: '1', company: 'Test Co', role: 'Dev', location: 'Remote', start_date: '2020-01-01', end_date: null, current: true, description: 'Test', tech_stack: ['Go'], order: 1, created_at: '', updated_at: '' },
]
const mockServices = [
{ id: '1', title: 'Web Dev', description: 'Desc', icon: 'code', features: ['React', 'Go'], order: 1, active: true, created_at: '', updated_at: '' },
]
describe('App Integration Tests', () => {
beforeEach(() => {
resetFetchMock()
})
it('renders all sections without errors', async () => {
mockFetchResponse(mockSkills)
mockFetchResponse(mockProjects)
mockFetchResponse(mockProjects)
mockFetchResponse(mockExperience)
mockFetchResponse(mockServices)
renderApp()
await waitFor(() => {
expect(screen.getByText('Services & Expertise')).toBeInTheDocument()
expect(screen.getByText('Selected Projects')).toBeInTheDocument()
expect(screen.getByText('Technical Skills')).toBeInTheDocument()
expect(screen.getByText('Experience')).toBeInTheDocument()
expect(screen.getByText("Let's Build Something Great")).toBeInTheDocument()
}, { timeout: 15000 })
})
it('does not make excessive API calls on initial render', async () => {
mockFetchResponse([])
mockFetchResponse([])
mockFetchResponse([])
mockFetchResponse([])
mockFetchResponse([])
renderApp()
await waitFor(() => {
expect(screen.getByText('Services & Expertise')).toBeInTheDocument()
}, { timeout: 15000 })
const calls = getFetchCalls()
expect(calls.length).toBeLessThanOrEqual(6)
})
it('handles API errors gracefully', async () => {
;(globalThis.fetch as unknown as { mockResolvedValueOnce: (value: unknown) => void }).mockResolvedValueOnce({
ok: false,
json: () => Promise.resolve({ success: false, error: { code: 'INTERNAL_ERROR', message: 'Server error' } }),
})
;(globalThis.fetch as unknown as { mockResolvedValueOnce: (value: unknown) => void }).mockResolvedValueOnce({
ok: true,
json: () => Promise.resolve({ success: true, data: [] }),
})
mockFetchResponse([])
mockFetchResponse([])
mockFetchResponse([])
renderApp()
await waitFor(() => {
expect(screen.getByText('Services & Expertise')).toBeInTheDocument()
}, { timeout: 15000 })
expect(screen.getByText('Selected Projects')).toBeInTheDocument()
})
it('contact form submission works', async () => {
mockFetchResponse([])
mockFetchResponse([])
mockFetchResponse([])
mockFetchResponse([])
mockFetchResponse([])
mockFetchResponse({ id: '1', name: 'Test', email: 'test@test.com', subject: 'Test', message: 'Test message', status: 'new', created_at: '', updated_at: '' })
renderApp()
await waitFor(() => {
expect(screen.getByText("Let's Build Something Great")).toBeInTheDocument()
}, { timeout: 15000 })
// Use getByPlaceholderText for more specific targeting
const nameInput = screen.getByPlaceholderText('Your name')
const emailInput = screen.getByPlaceholderText('your@email.com')
const subjectInput = screen.getByPlaceholderText("What's this about?")
const messageInput = screen.getByPlaceholderText("Tell us about your project, timeline, budget, or just say hello...")
const submitButton = screen.getByRole('button', { name: /send message/i })
await userEvent.type(nameInput, 'John Doe')
await userEvent.type(emailInput, 'john@example.com')
await userEvent.type(subjectInput, 'Project Inquiry')
await userEvent.type(messageInput, 'Hello, I would like to discuss a project.')
await act(async () => {
await userEvent.click(submitButton)
})
await waitFor(() => {
expect(screen.getByText('Message Sent!')).toBeInTheDocument()
}, { timeout: 15000 })
})
})