Files
euchre_camp/src/__tests__/EditTournamentForm.test.tsx
T
david bb6be245b7
Release / release (push) Failing after 9s
Build CI Images / build-ci-base (push) Failing after 18s
feat: Implement tournament schedule tab and fix E2E tests (#27)
## Summary

This PR implements the tournament schedule tab functionality and fixes all remaining E2E test failures.

### Changes Included

1. **Tournament Schedule Feature**
   - Added tournament schedule page at `/admin/tournaments/[id]/schedule`
   - Implemented "Generate Schedule" button functionality
   - Added schedule generation logic for round-robin tournaments

2. **E2E Test Fixes**
   - Fixed database connection issues in production builds
   - Improved test reliability with better error handling and debugging
   - Updated test infrastructure to use environment variables instead of hardcoded values

3. **CI/CD Updates**
   - Added E2E test job to PR workflow
   - Configured tests to run against development database
   - Moved database password to Gitea secrets

4. **Code Quality**
   - Removed hardcoded passwords from codebase
   - Improved Prisma client configuration
   - Enhanced authentication and navigation components

### Test Results
All 16 E2E test scenarios are now passing:
- Authentication tests: 
- Registration tests: 
- Tournament schedule tests: 
- Player schedule tests: 
- Admin navigation tests: 

### Database Configuration
- Tests run against `euchre_camp_dev` database
- Production builds use environment variables for database configuration
- Database password stored in Gitea secrets as `DB_PASSWORD`

### CI Pipeline
The PR workflow now includes:
1. Unit tests
2. E2E tests (using production build)
3. Version bump analysis

E2E tests must pass before PR can be merged.

Reviewed-on: #27
Co-authored-by: David Gwilliam <dhgwilliam@gmail.com>
Co-committed-by: David Gwilliam <dhgwilliam@gmail.com>
2026-04-27 01:59:16 +00:00

144 lines
4.2 KiB
TypeScript

import { describe, it, expect, mock, beforeEach } from 'bun:test'
import { render, screen, waitFor } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import EditTournamentForm from '@/components/EditTournamentForm'
// Mock next/navigation
mock.module('next/navigation', () => ({
useRouter: () => ({
push: mock(() => {}),
}),
}))
// Mock next/link
mock.module('next/link', () => ({
default: ({ children, href }: { children: React.ReactNode; href: string }) => (
<a href={href}>{children}</a>
),
}))
// Mock fetch
const mockFetch = mock(async () => new Response())
global.fetch = mockFetch as any
const mockTournament = {
id: 1,
event_id: null,
name: 'Test Tournament',
description: 'A test tournament',
eventDate: new Date('2024-01-15'),
eventType: 'tournament',
tournamentType: 'individual',
format: 'round_robin',
status: 'planned',
maxParticipants: 16,
ownerId: null,
targetScore: null,
allowTies: false,
createdAt: new Date(),
updatedAt: new Date(),
teamDurability: 'permanent',
partnerRotation: 'none',
allowByes: true,
teamConfiguration: null,
maxRosterChanges: null,
requireAdminVerify: false,
}
describe('EditTournamentForm', () => {
beforeEach(() => {
// Only clear the specific mocks we create, not global module mocks
mockFetch.mockClear()
})
it('renders form with initial values', () => {
render(<EditTournamentForm tournament={mockTournament} />)
expect(screen.getByLabelText('Tournament Name')).toHaveValue('Test Tournament')
expect(screen.getByLabelText('Description')).toHaveValue('A test tournament')
expect(screen.getByLabelText('Date')).toHaveValue('2024-01-15')
expect(screen.getByLabelText('Event Type')).toHaveValue('tournament')
expect(screen.getByLabelText('Format')).toHaveValue('round_robin')
expect(screen.getByLabelText('Status')).toHaveValue('planned')
expect(screen.getByLabelText('Max Participants (optional)')).toHaveValue(16)
})
it('updates form values on change', async () => {
const user = userEvent.setup()
render(<EditTournamentForm tournament={mockTournament} />)
const nameInput = screen.getByLabelText('Tournament Name')
await user.clear(nameInput)
await user.type(nameInput, 'Updated Tournament')
expect(nameInput).toHaveValue('Updated Tournament')
})
it('submits form with updated data', async () => {
const user = userEvent.setup()
mockFetch.mockResolvedValueOnce({
ok: true,
json: async () => ({ success: true }),
} as Response)
render(<EditTournamentForm tournament={mockTournament} />)
const nameInput = screen.getByLabelText('Tournament Name')
await user.clear(nameInput)
await user.type(nameInput, 'Updated Tournament')
const submitButton = screen.getByText('Save Changes')
await user.click(submitButton)
await waitFor(() => {
expect(mockFetch).toHaveBeenCalledWith(
'/api/tournaments/1',
expect.objectContaining({
method: 'PUT',
body: expect.stringContaining('Updated Tournament'),
})
)
})
})
it('shows error message on failed submission', async () => {
const user = userEvent.setup()
mockFetch.mockResolvedValueOnce({
ok: false,
json: async () => ({ error: 'Failed to update tournament' }),
} as Response)
render(<EditTournamentForm tournament={mockTournament} />)
const submitButton = screen.getByText('Save Changes')
await user.click(submitButton)
await waitFor(() => {
expect(screen.getByText('Failed to update tournament')).toBeInTheDocument()
})
})
it('disables submit button while loading', async () => {
const user = userEvent.setup()
mockFetch.mockImplementation(
() =>
new Promise((resolve) => {
setTimeout(() => {
resolve({
ok: true,
json: async () => ({ success: true }),
} as Response)
}, 100)
})
)
render(<EditTournamentForm tournament={mockTournament} />)
const submitButton = screen.getByText('Save Changes')
await user.click(submitButton)
expect(submitButton).toBeDisabled()
expect(submitButton).toHaveTextContent('Saving...')
})
})