caro5 / client /src /pages /__tests__ /LocalSetup.test.tsx
Pedro de Carvalho
Update pages
4dfd938
Raw
History Blame Contribute Delete
10.7 kB
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
import { render, screen, cleanup, fireEvent } from '@testing-library/react'
import LocalSetup from '../LocalSetup.tsx'
const { mockNavigate, mockSetPlayerName, mockSetPlayer2Name, mockSetPlayer1Symbol, mockSetPlayer2Symbol, mockSetPlayerColor, mockSetPlayer2Color, mockSetPlayerDrawTower, mockSetPlayer2DrawTower } = vi.hoisted(() => ({
mockNavigate: vi.fn(),
mockSetPlayerName: vi.fn(),
mockSetPlayer2Name: vi.fn(),
mockSetPlayer1Symbol: vi.fn(),
mockSetPlayer2Symbol: vi.fn(),
mockSetPlayerColor: vi.fn(),
mockSetPlayer2Color: vi.fn(),
mockSetPlayerDrawTower: vi.fn(),
mockSetPlayer2DrawTower: vi.fn(),
}))
const mockLocation = { state: { rules: { swap2: true, noOverlines: true, boardSize: 15 } } }
vi.mock('react-router-dom', () => ({
useNavigate: () => mockNavigate,
useLocation: () => mockLocation,
}))
vi.mock('../../theme.ts', () => ({
theme: {
bgStr: '#0d1117',
text: '#e6edf3',
textMuted: '#8b949e',
bgPanel: 'rgba(22,27,34,0.92)',
border: '#21262d',
player1Str: '#58a6ff',
player2Str: '#f0883e',
},
THEMES: { midnight: { label: 'Midnight', emoji: '🌙' } },
useThemeMode: vi.fn(() => ['midnight', vi.fn()]),
getPlayerName: vi.fn(() => ''),
getPlayer2Name: vi.fn(() => ''),
setPlayerName: mockSetPlayerName,
setPlayer2Name: mockSetPlayer2Name,
getPlayer1Symbol: vi.fn(() => 'X'),
getPlayer2Symbol: vi.fn(() => 'O'),
setPlayer1Symbol: mockSetPlayer1Symbol,
setPlayer2Symbol: mockSetPlayer2Symbol,
getPlayerColor: vi.fn(() => null),
setPlayerColor: mockSetPlayerColor,
getPlayer2Color: vi.fn(() => null),
setPlayer2Color: mockSetPlayer2Color,
getPlayerDrawTower: vi.fn(() => true),
setPlayerDrawTower: mockSetPlayerDrawTower,
getPlayer2DrawTower: vi.fn(() => false),
setPlayer2DrawTower: mockSetPlayer2DrawTower,
}))
vi.mock('../../components/ThemeSelector.tsx', () => ({
default: () => null,
}))
vi.mock('../../components/SymbolPicker.tsx', () => ({
default: ({ current, onSelect }: { current: string; onSelect: (s: string) => void }) => (
<button data-testid="symbol-picker" data-current={current} onClick={() => onSelect('★')}>
Pick Symbol
</button>
),
}))
afterEach(cleanup)
beforeEach(() => {
vi.clearAllMocks()
})
describe('LocalSetup page', () => {
it('renders the title', () => {
render(<LocalSetup />)
expect(screen.getByText('Local Game Setup')).toBeInTheDocument()
})
it('renders Player 1 and Player 2 labels', () => {
render(<LocalSetup />)
expect(screen.getByText('Player 1')).toBeInTheDocument()
expect(screen.getByText('Player 2')).toBeInTheDocument()
})
it('renders two name inputs', () => {
render(<LocalSetup />)
const inputs = screen.getAllByPlaceholderText('Enter name')
expect(inputs).toHaveLength(2)
})
it('renders symbol buttons for both players', () => {
render(<LocalSetup />)
const symbols = screen.getAllByText(/^[XO]$/)
expect(symbols).toHaveLength(2)
})
it('renders Start Game button', () => {
render(<LocalSetup />)
expect(screen.getByText('Start Game')).toBeInTheDocument()
})
it('renders Back button', () => {
render(<LocalSetup />)
expect(screen.getByText('← Back to menu')).toBeInTheDocument()
})
it('navigates back on Back click', () => {
render(<LocalSetup />)
fireEvent.click(screen.getByText('← Back to menu'))
expect(mockNavigate).toHaveBeenCalledWith('/')
})
it('starts game with default names when inputs are empty', () => {
render(<LocalSetup />)
fireEvent.click(screen.getByText('Start Game'))
expect(mockSetPlayerName).toHaveBeenCalledWith('Caro 1')
expect(mockSetPlayer2Name).toHaveBeenCalledWith('Caro 2')
expect(mockSetPlayer1Symbol).toHaveBeenCalledWith('X')
expect(mockSetPlayer2Symbol).toHaveBeenCalledWith('O')
expect(mockNavigate).toHaveBeenCalledWith('/play/local', {
state: {
player1Name: 'Caro 1',
player2Name: 'Caro 2',
player1Symbol: 'X',
player2Symbol: 'O',
player1Type: 'human',
player2Type: 'bot',
botProfile: 'normal',
opponentType: 'taunt',
tauntStyle: 'funny',
rules: { swap2: true, noOverlines: true, boardSize: 15 },
},
})
})
it('starts game with custom names', () => {
render(<LocalSetup />)
const inputs = screen.getAllByPlaceholderText('Enter name')
fireEvent.change(inputs[0], { target: { value: 'Alice' } })
fireEvent.change(inputs[1], { target: { value: 'Bob' } })
fireEvent.click(screen.getByText('Start Game'))
expect(mockSetPlayerName).toHaveBeenCalledWith('Alice')
expect(mockSetPlayer2Name).toHaveBeenCalledWith('Bob')
expect(mockSetPlayer1Symbol).toHaveBeenCalledWith('X')
expect(mockSetPlayer2Symbol).toHaveBeenCalledWith('O')
expect(mockNavigate).toHaveBeenCalledWith('/play/local', {
state: {
player1Name: 'Alice',
player2Name: 'Bob',
player1Symbol: 'X',
player2Symbol: 'O',
player1Type: 'human',
player2Type: 'bot',
botProfile: 'normal',
opponentType: 'taunt',
tauntStyle: 'funny',
rules: { swap2: true, noOverlines: true, boardSize: 15 },
},
})
})
it('opens symbol picker for Player 1', () => {
render(<LocalSetup />)
const symBtns = screen.getAllByText(/^[XO]$/)
fireEvent.click(symBtns[0])
expect(screen.getByTestId('symbol-picker')).toBeInTheDocument()
})
it('changes Player 1 symbol via picker', () => {
render(<LocalSetup />)
const symBtns = screen.getAllByText(/^[XO]$/)
fireEvent.click(symBtns[0])
fireEvent.click(screen.getByText('Pick Symbol'))
expect(screen.getByText('★')).toBeInTheDocument()
})
it('opens symbol picker for Player 2', () => {
render(<LocalSetup />)
const symBtns = screen.getAllByText(/^[XO]$/)
fireEvent.click(symBtns[1])
expect(screen.getByTestId('symbol-picker')).toBeInTheDocument()
})
it('changes Player 2 symbol via picker', () => {
render(<LocalSetup />)
const symBtns = screen.getAllByText(/^[XO]$/)
fireEvent.click(symBtns[1])
fireEvent.click(screen.getByText('Pick Symbol'))
expect(screen.getAllByText('★')).toHaveLength(1)
})
it('renders theme selector button', () => {
render(<LocalSetup />)
expect(screen.getByText('🌙')).toBeInTheDocument()
})
it('lets players choose human or bot type', () => {
render(<LocalSetup />)
expect(screen.getAllByText('Human')).toHaveLength(2)
expect(screen.getAllByText('Bot')).toHaveLength(2)
expect(screen.getByText('Bot vs Bot')).toBeInTheDocument()
})
it('renders read-only rules summary', () => {
render(<LocalSetup />)
expect(screen.getByText('GAME RULES')).toBeInTheDocument()
expect(screen.getByText('Swap2 Enabled')).toBeInTheDocument()
expect(screen.getByText('No overline (6+)')).toBeInTheDocument()
expect(screen.getByText('Board 15×15')).toBeInTheDocument()
})
it('sets the bot profile for local bot games', () => {
render(<LocalSetup />)
expect(screen.getByText('Difficulty')).toBeInTheDocument()
fireEvent.click(screen.getByText('Expert'))
fireEvent.click(screen.getByText('Start Game'))
expect(mockNavigate).toHaveBeenCalledWith('/play/local', {
state: {
player1Name: 'Caro 1',
player2Name: 'Caro 2',
player1Symbol: 'X',
player2Symbol: 'O',
player1Type: 'human',
player2Type: 'bot',
botProfile: 'expert',
opponentType: 'taunt',
tauntStyle: 'funny',
rules: { swap2: true, noOverlines: true, boardSize: 15 },
},
})
})
it('sets up bot vs bot defaults', () => {
render(<LocalSetup />)
fireEvent.click(screen.getByText('Bot vs Bot'))
fireEvent.click(screen.getByText('Start Game'))
expect(mockNavigate).toHaveBeenCalledWith('/play/local', {
state: {
player1Name: 'Bot 1',
player2Name: 'Bot 2',
player1Symbol: 'X',
player2Symbol: 'O',
player1Type: 'bot',
player2Type: 'bot',
botProfile: 'normal',
opponentType: 'taunt',
tauntStyle: 'funny',
rules: { swap2: true, noOverlines: true, boardSize: 15 },
},
})
})
it('allows choosing opponent type and personality style', () => {
render(<LocalSetup />)
expect(screen.getByText('Opponent Type')).toBeInTheDocument()
fireEvent.click(screen.getByText(/streamer/i))
expect(screen.queryByText('Personality')).not.toBeInTheDocument()
fireEvent.click(screen.getByText(/taunt/i))
expect(screen.getByText('Personality')).toBeInTheDocument()
fireEvent.click(screen.getByText(/lover/i))
fireEvent.click(screen.getByText('Start Game'))
expect(mockNavigate).toHaveBeenCalledWith('/play/local', {
state: {
player1Name: 'Caro 1',
player2Name: 'Caro 2',
player1Symbol: 'X',
player2Symbol: 'O',
player1Type: 'human',
player2Type: 'bot',
botProfile: 'normal',
opponentType: 'taunt',
tauntStyle: 'lover',
rules: { swap2: true, noOverlines: true, boardSize: 15 },
},
})
})
it('swaps player configurations including display tower states', () => {
render(<LocalSetup />)
const inputs = screen.getAllByPlaceholderText('Enter name')
fireEvent.change(inputs[0], { target: { value: 'Alice' } })
fireEvent.change(inputs[1], { target: { value: 'Bob' } })
const swapBtn = screen.getByTitle('Swap Players')
fireEvent.click(swapBtn)
expect(inputs[0]).toHaveValue('Bob')
expect(inputs[1]).toHaveValue('Alice')
fireEvent.click(screen.getByText('Start Game'))
expect(mockSetPlayerDrawTower).toHaveBeenCalledWith(false)
expect(mockSetPlayer2DrawTower).toHaveBeenCalledWith(true)
})
it('selects colors and starts game with selected colors', () => {
render(<LocalSetup />)
const buttons = screen.getAllByRole('button')
const p1RedBtn = buttons.find(b => b.getAttribute('title') === '#ef4444')
expect(p1RedBtn).toBeDefined()
if (p1RedBtn) fireEvent.click(p1RedBtn)
fireEvent.click(screen.getByText('Start Game'))
expect(mockSetPlayerColor).toHaveBeenCalledWith('#ef4444')
expect(mockSetPlayer2Color).toHaveBeenCalledWith('#f0883e')
})
it('saves selected tower preferences on start game', () => {
render(<LocalSetup />)
fireEvent.click(screen.getByText('Start Game'))
expect(mockSetPlayerDrawTower).toHaveBeenCalledWith(true)
expect(mockSetPlayer2DrawTower).toHaveBeenCalledWith(false)
})
})