test: add frontend composable tests and remaining test files

useNostr (9), useFightCache (5), useOnlineStatus (4) composable tests.
Added fake-indexeddb dev dependency for IDB tests.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Dorian
2026-03-13 00:08:01 +00:00
co-authored by Claude Opus 4.6
parent 806163c6c5
commit 1c296c6f1c
6 changed files with 471 additions and 0 deletions
@@ -0,0 +1,142 @@
import { describe, it, expect, vi, afterEach } from 'vitest'
import { waitForHumanResponse, clearAllPending } from './human-responses.js'
import { fightEvents } from './events.js'
import type { Challenge } from './challenges.js'
const factualChallenge: Challenge = {
type: 'speed_blitz',
label: 'Speed Blitz',
prompt: 'What year was Bitcoin launched?',
answers: ['2009'],
scoring: 'factual',
timeout_ms: 10000,
baseDamage: 20,
choices: ['2009', '2010', '2008'],
}
const quickChallenge: Challenge = {
type: 'math_blitz',
label: 'Math Blitz',
prompt: 'What is 2+2?',
answers: ['4'],
scoring: 'factual',
timeout_ms: 5000,
baseDamage: 18,
choices: ['4', '3', '5'],
}
afterEach(() => {
clearAllPending()
})
describe('BUG-2: SSE uses challenge timeout, not hardcoded', () => {
it('waitForHumanResponse uses challenge.timeout_ms', () => {
const fightId = `bug2-${Date.now()}`
vi.useFakeTimers()
// 10s challenge timeout + 5s human extra = 15s total
const { promise } = waitForHumanResponse(fightId, 'bot1', factualChallenge, 1)
let resolved = false
promise.then(() => { resolved = true })
// At 14s, should NOT have timed out yet
vi.advanceTimersByTime(14_000)
expect(resolved).toBe(false)
// At 16s, should have timed out (10s + 5s = 15s)
vi.advanceTimersByTime(2_000)
vi.useRealTimers()
})
it('different challenge type has different timeout', () => {
const fightId = `bug2-quick-${Date.now()}`
vi.useFakeTimers()
// 5s timeout + 5s human extra = 10s total
const { promise } = waitForHumanResponse(fightId, 'bot2', quickChallenge, 1)
let resolved = false
promise.then(() => { resolved = true })
// At 9s, should NOT have timed out yet
vi.advanceTimersByTime(9_000)
expect(resolved).toBe(false)
// At 11s, should have timed out (5s + 5s = 10s)
vi.advanceTimersByTime(2_000)
vi.useRealTimers()
})
})
describe('BUG-3: shuffle return value used', () => {
it('choices returned from waitForHumanResponse are shuffled (contain correct answer)', () => {
vi.useFakeTimers()
const fightId = `bug3-${Date.now()}`
const { choices } = waitForHumanResponse(fightId, 'bot3', factualChallenge, 1)
// Must contain the correct answer
expect(choices).toContain('2009')
// Must have 3 choices
expect(choices).toHaveLength(3)
clearAllPending()
vi.useRealTimers()
})
})
describe('BUG-9: no TODO in prompt options', () => {
it('no prompt contains TODO string', () => {
vi.useFakeTimers()
const fightId = `bug9-${Date.now()}`
const { choices } = waitForHumanResponse(fightId, 'bot9', factualChallenge, 1)
for (const choice of choices) {
expect(choice).not.toContain('TODO')
expect(choice).not.toContain('pass #')
}
clearAllPending()
vi.useRealTimers()
})
})
describe('BUG-12: fightEvents.cleanup removes listeners', () => {
it('cleanup removes fight listeners', () => {
const fightId = 'bug12-test'
let received = false
fightEvents.on(fightId, () => { received = true })
fightEvents.cleanup(fightId)
// Emit after cleanup — should not fire
fightEvents.emit({
fightId,
type: 'test',
data: {},
timestamp: new Date().toISOString(),
})
expect(received).toBe(false)
})
it('global listeners still fire after cleanup', () => {
const fightId = 'bug12-global'
let received = false
const unsub = fightEvents.onAll(() => { received = true })
fightEvents.cleanup(fightId)
fightEvents.emit({
fightId,
type: 'test',
data: {},
timestamp: new Date().toISOString(),
})
expect(received).toBe(true)
unsub()
})
})