mirror of
https://github.com/kuhyx/testsAndMisc.git
synced 2026-11-11 14:27:49 +01:00
Haunting, Normal and Crusade scale enemy hp and speed, the survivor's health, your income, and the length of the night. Normal is exactly 1.0 on every multiplier — it is the identity tier, which is what lets every existing raw-value assertion keep passing untouched. Enemy speed now derives from a single helper, so difficulty and frenzy compose in one place rather than fighting at three call sites. Also fixes a bug this surfaced: handleRestart ignored the selected tier, so restarting after choosing Crusade silently reverted to Normal. The react-hooks purity rule flagged Date.now() in the new handler, and extracting restartSeed() exposed the omission. Endless mode is deliberately left out — fmtTime(Infinity) renders Infinity:NaN, which would cost a guard plus tests for a mode whose only content is the same game but longer. README rewritten for all four features, including the reversal of its old placement promise. 160 tests, 100% coverage. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VFyXexNjMQG9V7K8de17fc
91 lines
3.1 KiB
TypeScript
91 lines
3.1 KiB
TypeScript
import { act, fireEvent, render, screen } from '@testing-library/react'
|
|
import { afterEach, describe, expect, it, vi } from 'vitest'
|
|
import { App } from './App'
|
|
import { makeEnemy } from './core/sim'
|
|
import type { RafHarness } from './test/harness'
|
|
import { installRaf, makeCtxStub } from './test/harness'
|
|
|
|
afterEach(() => {
|
|
vi.unstubAllGlobals()
|
|
vi.restoreAllMocks()
|
|
})
|
|
|
|
const pump = (raf: RafHarness, ts: number): void => {
|
|
act(() => {
|
|
raf.pump(ts)
|
|
})
|
|
}
|
|
|
|
describe('App', () => {
|
|
it('runs the loop and accrues souls even without a canvas context', () => {
|
|
const raf = installRaf()
|
|
render(<App />)
|
|
expect(screen.getByText('30 / 400')).toBeInTheDocument()
|
|
pump(raf, 0)
|
|
for (let i = 1; i <= 20; i += 1) {
|
|
pump(raf, i * 1000)
|
|
}
|
|
expect(screen.getByText('42 / 400')).toBeInTheDocument()
|
|
expect(screen.getByText(/0:02 \/ 5:00/)).toBeInTheDocument()
|
|
})
|
|
|
|
it('spends souls on a spawn and draws the horde', () => {
|
|
const raf = installRaf()
|
|
const stub = makeCtxStub()
|
|
vi.spyOn(HTMLCanvasElement.prototype, 'getContext')
|
|
.mockImplementation(() => stub as unknown as CanvasRenderingContext2D)
|
|
render(<App seed={5} />)
|
|
fireEvent.click(screen.getByRole('button', { name: /rusher/i }))
|
|
pump(raf, 0)
|
|
expect(screen.getByText('20 / 400')).toBeInTheDocument()
|
|
const arcsWithHorde = stub.arc.mock.calls.length
|
|
expect(arcsWithHorde).toBeGreaterThan(4)
|
|
})
|
|
|
|
it('shows the director victory overlay and restarts fresh', () => {
|
|
const raf = installRaf()
|
|
render(
|
|
<App
|
|
seed={9}
|
|
boot={(state) => {
|
|
state.survivor.hp = 5
|
|
makeEnemy(state, 'rusher', { x: state.survivor.pos.x, y: state.survivor.pos.y })
|
|
}}
|
|
/>,
|
|
)
|
|
pump(raf, 0)
|
|
expect(screen.getByText('The intruder falls')).toBeInTheDocument()
|
|
fireEvent.click(screen.getByRole('button', { name: /open the gates/i }))
|
|
expect(screen.queryByText('The intruder falls')).not.toBeInTheDocument()
|
|
expect(screen.getByText('30 / 400')).toBeInTheDocument()
|
|
})
|
|
|
|
it('restarts on a new tier when the difficulty is changed', () => {
|
|
const raf = installRaf()
|
|
render(<App seed={9} />)
|
|
pump(raf, 0)
|
|
// Normal runs 5:00; Crusade runs 6:00, so the clock proves the tier took.
|
|
expect(screen.getByText(/\/ 5:00/)).toBeInTheDocument()
|
|
fireEvent.click(screen.getByRole('button', { name: 'Crusade' }))
|
|
expect(screen.getByText(/\/ 6:00/)).toBeInTheDocument()
|
|
expect(screen.getByRole('button', { name: 'Crusade' })).toHaveClass('diff-on')
|
|
})
|
|
|
|
it('dispatches a director power from the bar', () => {
|
|
const raf = installRaf()
|
|
render(<App seed={9} boot={(state) => { state.director.energy = 400 }} />)
|
|
pump(raf, 0)
|
|
fireEvent.click(screen.getByRole('button', { name: /frenzy/i }))
|
|
pump(raf, 16)
|
|
expect(screen.getByText(/frenzied/i)).toBeInTheDocument()
|
|
})
|
|
|
|
it('shows the dawn overlay when the survivor outlasts the horde', () => {
|
|
const raf = installRaf()
|
|
render(<App boot={(state) => { state.duration = 0.05 }} />)
|
|
pump(raf, 0)
|
|
pump(raf, 100)
|
|
expect(screen.getByText('Dawn breaks')).toBeInTheDocument()
|
|
})
|
|
})
|