Raise frontend branch coverage from 86.84% to 93.78%
Branches were the weakest metric and had the least headroom above the 85% floor. These are tests for decision paths that were genuinely unexercised, not padding. The largest gaps were systemic rather than per-file: the `err instanceof Error ? ... : fallback` branch was untested across eight data hooks, useVacationDays had no dedicated test file at all, and Calendar.tsx's missing-workspace/child/personalData fallbacks were never hit. Left deliberately uncovered, with reasons: `?? fallback` guards whose fallback cannot occur given how the value is constructed upstream, and guards behind buttons that are disabled whenever the guard would matter. Reaching those means calling handlers directly, which tests the test rather than the app. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RXMhc9nT6sNz1SL4j8dznL
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import { describe, it, expect, vi } from 'vitest'
|
||||
import { render, screen } from '@testing-library/react'
|
||||
import userEvent from '@testing-library/user-event'
|
||||
import { BrowserRouter } from 'react-router-dom'
|
||||
import { BrowserRouter, MemoryRouter } from 'react-router-dom'
|
||||
import { Header } from './Header'
|
||||
|
||||
const renderWithRouter = (element: React.ReactElement) => {
|
||||
@@ -12,6 +12,14 @@ const renderWithRouter = (element: React.ReactElement) => {
|
||||
)
|
||||
}
|
||||
|
||||
const renderAtPath = (element: React.ReactElement, path: string) => {
|
||||
return render(
|
||||
<MemoryRouter initialEntries={[path]}>
|
||||
{element}
|
||||
</MemoryRouter>,
|
||||
)
|
||||
}
|
||||
|
||||
describe('Header', () => {
|
||||
it('renders header with default title', () => {
|
||||
renderWithRouter(<Header />)
|
||||
@@ -75,4 +83,39 @@ describe('Header', () => {
|
||||
renderWithRouter(<Header />)
|
||||
expect(screen.queryByTestId('logout-button')).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('does not crash and renders the select when selectedChildId is not provided (falls back to "")', () => {
|
||||
const childrenList = [
|
||||
{ id: '1', name: 'Emma' },
|
||||
{ id: '2', name: 'Oscar' },
|
||||
]
|
||||
renderWithRouter(<Header childrenList={childrenList} />)
|
||||
const select = screen.getByTestId('child-select') as HTMLSelectElement
|
||||
// selectedChildId ?? '' binds the select to a value ('') that matches no
|
||||
// <option>, so the browser falls back to the first option — it must not
|
||||
// be coerced to something else (e.g. "undefined" or a stale selection).
|
||||
expect(select.value).toBe('1')
|
||||
})
|
||||
|
||||
it('marks the calendar link active on the calendar route', () => {
|
||||
renderAtPath(<Header />, '/calendar')
|
||||
expect(screen.getByText('Kalender')).toHaveClass('header__nav-link--active')
|
||||
expect(screen.getByText('Översikt')).not.toHaveClass('header__nav-link--active')
|
||||
})
|
||||
|
||||
it('marks the plan link active on the plan route', () => {
|
||||
renderAtPath(<Header />, '/plan')
|
||||
expect(screen.getByText('Planering')).toHaveClass('header__nav-link--active')
|
||||
})
|
||||
|
||||
it('marks the tips link active on the tips route', () => {
|
||||
renderAtPath(<Header />, '/tips')
|
||||
expect(screen.getByText('Tips')).toHaveClass('header__nav-link--active')
|
||||
})
|
||||
|
||||
it('marks the settings link active on the settings route, and the overview link inactive', () => {
|
||||
renderAtPath(<Header />, '/settings')
|
||||
expect(screen.getByText('Inställningar')).toHaveClass('header__nav-link--active')
|
||||
expect(screen.getByText('Översikt')).not.toHaveClass('header__nav-link--active')
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user