Add component tests for EndRunModal, GameGrid, RulesConfiguration, Layout
33 tests covering rendering, user interactions (userEvent clicks), prop callbacks, filter state, and conditional description text. Adds a matchMedia stub to the vitest setup file so components importing useTheme don't throw in jsdom. Also adds actionlint and zizmor pre-commit hooks for GitHub Actions linting. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
115
frontend/src/components/GameGrid.test.tsx
Normal file
115
frontend/src/components/GameGrid.test.tsx
Normal file
@@ -0,0 +1,115 @@
|
||||
import { render, screen } from '@testing-library/react'
|
||||
import userEvent from '@testing-library/user-event'
|
||||
import type { Game } from '../types'
|
||||
import { GameGrid } from './GameGrid'
|
||||
|
||||
const RED: Game = {
|
||||
id: 1,
|
||||
name: 'Pokemon Red',
|
||||
slug: 'red',
|
||||
generation: 1,
|
||||
region: 'kanto',
|
||||
category: null,
|
||||
boxArtUrl: null,
|
||||
color: null,
|
||||
releaseYear: null,
|
||||
versionGroupId: 1,
|
||||
}
|
||||
|
||||
const GOLD: Game = {
|
||||
id: 2,
|
||||
name: 'Pokemon Gold',
|
||||
slug: 'gold',
|
||||
generation: 2,
|
||||
region: 'johto',
|
||||
category: null,
|
||||
boxArtUrl: null,
|
||||
color: null,
|
||||
releaseYear: null,
|
||||
versionGroupId: 2,
|
||||
}
|
||||
|
||||
const RUBY: Game = {
|
||||
id: 3,
|
||||
name: 'Pokemon Ruby',
|
||||
slug: 'ruby',
|
||||
generation: 3,
|
||||
region: 'hoenn',
|
||||
category: null,
|
||||
boxArtUrl: null,
|
||||
color: null,
|
||||
releaseYear: null,
|
||||
versionGroupId: 3,
|
||||
}
|
||||
|
||||
function setup(overrides: Partial<React.ComponentProps<typeof GameGrid>> = {}) {
|
||||
const props = {
|
||||
games: [RED, GOLD, RUBY],
|
||||
selectedId: null,
|
||||
onSelect: vi.fn(),
|
||||
...overrides,
|
||||
}
|
||||
render(<GameGrid {...props} />)
|
||||
return props
|
||||
}
|
||||
|
||||
describe('GameGrid', () => {
|
||||
it('renders all game names', () => {
|
||||
setup()
|
||||
expect(screen.getByText('Pokemon Red')).toBeInTheDocument()
|
||||
expect(screen.getByText('Pokemon Gold')).toBeInTheDocument()
|
||||
expect(screen.getByText('Pokemon Ruby')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('renders generation filter pills for each unique generation', () => {
|
||||
setup()
|
||||
expect(screen.getByRole('button', { name: 'Gen 1' })).toBeInTheDocument()
|
||||
expect(screen.getByRole('button', { name: 'Gen 2' })).toBeInTheDocument()
|
||||
expect(screen.getByRole('button', { name: 'Gen 3' })).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('filters games when a generation pill is clicked', async () => {
|
||||
setup()
|
||||
await userEvent.click(screen.getByRole('button', { name: 'Gen 1' }))
|
||||
expect(screen.getByText('Pokemon Red')).toBeInTheDocument()
|
||||
expect(screen.queryByText('Pokemon Gold')).not.toBeInTheDocument()
|
||||
expect(screen.queryByText('Pokemon Ruby')).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('restores all games when "All" generation pill is clicked', async () => {
|
||||
setup()
|
||||
await userEvent.click(screen.getByRole('button', { name: 'Gen 1' }))
|
||||
await userEvent.click(screen.getAllByRole('button', { name: 'All' })[0]!)
|
||||
expect(screen.getByText('Pokemon Red')).toBeInTheDocument()
|
||||
expect(screen.getByText('Pokemon Gold')).toBeInTheDocument()
|
||||
expect(screen.getByText('Pokemon Ruby')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('filters games when a region pill is clicked', async () => {
|
||||
setup()
|
||||
await userEvent.click(screen.getByRole('button', { name: 'Johto' }))
|
||||
expect(screen.queryByText('Pokemon Red')).not.toBeInTheDocument()
|
||||
expect(screen.getByText('Pokemon Gold')).toBeInTheDocument()
|
||||
expect(screen.queryByText('Pokemon Ruby')).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('calls onSelect with the game when a game card is clicked', async () => {
|
||||
const { onSelect } = setup()
|
||||
await userEvent.click(screen.getByText('Pokemon Red'))
|
||||
expect(onSelect).toHaveBeenCalledWith(RED)
|
||||
})
|
||||
|
||||
it('hides games with active runs when the checkbox is ticked', async () => {
|
||||
setup({
|
||||
runs: [{ id: 10, gameId: 1, status: 'active' } as never],
|
||||
})
|
||||
await userEvent.click(screen.getByLabelText(/hide games with active run/i))
|
||||
expect(screen.queryByText('Pokemon Red')).not.toBeInTheDocument()
|
||||
expect(screen.getByText('Pokemon Gold')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('does not render run-based checkboxes when runs prop is omitted', () => {
|
||||
setup({ runs: undefined })
|
||||
expect(screen.queryByLabelText(/hide games with active run/i)).not.toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user