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:
61
frontend/src/components/Layout.test.tsx
Normal file
61
frontend/src/components/Layout.test.tsx
Normal file
@@ -0,0 +1,61 @@
|
||||
import { render, screen } from '@testing-library/react'
|
||||
import userEvent from '@testing-library/user-event'
|
||||
import { MemoryRouter } from 'react-router-dom'
|
||||
import { Layout } from './Layout'
|
||||
|
||||
vi.mock('../hooks/useTheme', () => ({
|
||||
useTheme: () => ({ theme: 'dark' as const, toggle: vi.fn() }),
|
||||
}))
|
||||
|
||||
function renderLayout(initialPath = '/') {
|
||||
return render(
|
||||
<MemoryRouter initialEntries={[initialPath]}>
|
||||
<Layout />
|
||||
</MemoryRouter>
|
||||
)
|
||||
}
|
||||
|
||||
describe('Layout', () => {
|
||||
it('renders all desktop navigation links', () => {
|
||||
renderLayout()
|
||||
expect(screen.getAllByRole('link', { name: /new run/i })[0]).toBeInTheDocument()
|
||||
expect(screen.getAllByRole('link', { name: /my runs/i })[0]).toBeInTheDocument()
|
||||
expect(screen.getAllByRole('link', { name: /genlockes/i })[0]).toBeInTheDocument()
|
||||
expect(screen.getAllByRole('link', { name: /stats/i })[0]).toBeInTheDocument()
|
||||
expect(screen.getAllByRole('link', { name: /admin/i })[0]).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('renders the brand logo link', () => {
|
||||
renderLayout()
|
||||
expect(screen.getByRole('link', { name: /ant/i })).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('renders the theme toggle button', () => {
|
||||
renderLayout()
|
||||
expect(screen.getAllByRole('button', { name: /switch to light mode/i })[0]).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('initially hides the mobile dropdown menu', () => {
|
||||
renderLayout()
|
||||
// Mobile menu items exist in DOM but menu is hidden; the mobile dropdown
|
||||
// only appears inside the sm:hidden block after state toggle.
|
||||
// The hamburger button should be present.
|
||||
expect(screen.getByRole('button', { name: /toggle menu/i })).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('shows the mobile dropdown when the hamburger is clicked', async () => {
|
||||
renderLayout()
|
||||
const hamburger = screen.getByRole('button', { name: /toggle menu/i })
|
||||
await userEvent.click(hamburger)
|
||||
// After click, the menu open state adds a dropdown with nav links
|
||||
// We can verify the menu is open by checking a class change or that
|
||||
// the nav links appear in the mobile dropdown section.
|
||||
// The mobile dropdown renders navLinks in a div inside sm:hidden
|
||||
expect(screen.getAllByRole('link', { name: /my runs/i }).length).toBeGreaterThan(1)
|
||||
})
|
||||
|
||||
it('renders the footer with PokeDB attribution', () => {
|
||||
renderLayout()
|
||||
expect(screen.getByRole('link', { name: /pokedb/i })).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user