steam-backlog-enforcer/web/src/components/SummaryCards.test.tsx
Krzysztof kuhy Rudnicki 7ac07c4b7a feat: add pick-manual command with 2-week enforcement lock
User can now pick any owned game by Steam app_id via `pick-manual <id>`.
The script resolves the game name, asks for YES confirmation, then locks
all other commands for 14 days or until the game is 100% complete.
Post-assignment steps (uninstall others, install, hide library) mirror
the automatic pick flow. Lock is checked before every command including
add-exception. Also fixes pre-existing test failures in hltb, stats,
and web_dataset modules and adds 100% coverage for all changed code.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-30 17:15:37 +02:00

77 lines
3.0 KiB
TypeScript

import { render, screen } from '@testing-library/react'
import { describe, expect, it } from 'vitest'
import { applyFilters } from '../estimate'
import type { Filters } from '../types'
import { makeDataset, makeFilters, makeGame, makePaceVsHltb, makeState } from '../test/factories'
import { SummaryCards } from './SummaryCards'
function renderCards(filtersOver: Partial<Filters> = {}, statePace = 0, paceVsHltb = null) {
const filters = makeFilters(filtersOver)
const dataset = makeDataset([makeGame({ app_id: 1 })], { pace_vs_hltb: paceVsHltb })
const result = applyFilters(dataset, filters)
render(
<SummaryCards
result={result}
filters={filters}
state={makeState({ pace_games_per_day: statePace })}
presets={[2, 4, 6, 8]}
defaultQualifying={result.remainingGames}
paceVsHltb={paceVsHltb}
/>,
)
return result
}
describe('SummaryCards', () => {
it('shows the in-scope count and a CLI-parity match badge', () => {
renderCards()
expect(screen.getByText(/games in scope/i)).toBeInTheDocument()
expect(screen.getByText(/match/i)).toBeInTheDocument()
})
it('notes a missing start date in the pace card', () => {
renderCards({}, 0)
expect(screen.getByText(/No start date set/i)).toBeInTheDocument()
})
it('renders the target-date banner with required hours/day', () => {
renderCards({ targetDate: '2099-01-01', basis: 'leisure' })
const banner = document.querySelector('.target-banner')
expect(banner?.textContent).toMatch(/you need/i)
expect(banner?.textContent).toMatch(/h\/day/i)
})
it('renders the target banner in games/day for the pace basis', () => {
renderCards({ targetDate: '2099-01-01', basis: 'pace' }, 0.9)
const banner = document.querySelector('.target-banner')
expect(banner?.textContent).toMatch(/games\/day/i)
})
})
describe('PlayerSpeedInsight', () => {
it('shows empty state message when paceVsHltb is null', () => {
renderCards({}, 0, null)
expect(screen.getByText(/Your Play Style vs HLTB/i)).toBeInTheDocument()
expect(screen.getByText(/No calibration data yet/i)).toBeInTheDocument()
})
it('shows calibration stats when paceVsHltb is provided', () => {
renderCards({}, 0, makePaceVsHltb())
expect(screen.getByText(/Calibration games/i)).toBeInTheDocument()
expect(screen.getByText(/vs Rush speed/i)).toBeInTheDocument()
expect(screen.getByText('Play style')).toBeInTheDocument()
expect(screen.getByText(/Between rush and leisure/i)).toBeInTheDocument()
})
it('shows estimated total when calibration data is present', () => {
// rushTotal = 10, leisureTotal = 20, t = 0.05 → 10 + 0.05 * 10 = 10.5 h
renderCards({}, 0, makePaceVsHltb({ interpolation_t: 0.05 }))
expect(screen.getByText(/Estimated total at your pace/i)).toBeInTheDocument()
})
it('hides leisure ratio when ratio_vs_leisure is -1', () => {
renderCards({}, 0, makePaceVsHltb({ ratio_vs_leisure: -1, interpolation_t: -1 }))
expect(screen.queryByText(/vs Leisure speed/i)).not.toBeInTheDocument()
})
})