initial commit
This commit is contained in:
commit
e7987e88a7
70 changed files with 13661 additions and 0 deletions
129
web/src/__tests__/App.test.tsx
Normal file
129
web/src/__tests__/App.test.tsx
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
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 })
|
||||
})
|
||||
})
|
||||
Loading…
Add table
Add a link
Reference in a new issue