strat-gameplay-webapp/frontend-sba/tests/unit/composables/useDefensiveSetup.spec.ts

154 lines
4.1 KiB
TypeScript

import { describe, it, expect, beforeEach } from 'vitest'
import { useDefensiveSetup } from '~/composables/useDefensiveSetup'
describe('useDefensiveSetup', () => {
beforeEach(() => {
const { reset } = useDefensiveSetup()
reset()
})
describe('singleton behavior', () => {
it('returns the same state across multiple calls', () => {
/**
* The composable is a module-level singleton — multiple calls to
* useDefensiveSetup() should return refs pointing to the same state.
*/
const a = useDefensiveSetup()
const b = useDefensiveSetup()
a.toggleHold(1)
expect(b.isHeld(1)).toBe(true)
expect(b.holdRunnersArray.value).toEqual([1])
})
})
describe('toggleHold', () => {
it('adds a base when not held', () => {
const { toggleHold, isHeld } = useDefensiveSetup()
toggleHold(1)
expect(isHeld(1)).toBe(true)
})
it('removes a base when already held', () => {
const { toggleHold, isHeld } = useDefensiveSetup()
toggleHold(2)
expect(isHeld(2)).toBe(true)
toggleHold(2)
expect(isHeld(2)).toBe(false)
})
it('can hold multiple bases independently', () => {
const { toggleHold, isHeld, holdRunnersArray } = useDefensiveSetup()
toggleHold(1)
toggleHold(3)
expect(isHeld(1)).toBe(true)
expect(isHeld(2)).toBe(false)
expect(isHeld(3)).toBe(true)
expect(holdRunnersArray.value).toEqual([1, 3])
})
})
describe('holdRunnersArray', () => {
it('returns sorted array of held base numbers', () => {
/**
* holdRunnersArray should always be sorted so the output is
* deterministic regardless of toggle order.
*/
const { toggleHold, holdRunnersArray } = useDefensiveSetup()
toggleHold(3)
toggleHold(1)
expect(holdRunnersArray.value).toEqual([1, 3])
})
it('returns empty array when nothing is held', () => {
const { holdRunnersArray } = useDefensiveSetup()
expect(holdRunnersArray.value).toEqual([])
})
})
describe('reset', () => {
it('clears all hold state and resets depths to defaults', () => {
const { toggleHold, infieldDepth, outfieldDepth, holdRunnersArray, reset } = useDefensiveSetup()
toggleHold(1)
toggleHold(2)
infieldDepth.value = 'infield_in'
outfieldDepth.value = 'shallow'
reset()
expect(holdRunnersArray.value).toEqual([])
expect(infieldDepth.value).toBe('normal')
expect(outfieldDepth.value).toBe('normal')
})
})
describe('syncFromDecision', () => {
it('sets all state from a DefensiveDecision object', () => {
const { syncFromDecision, infieldDepth, outfieldDepth, holdRunnersArray } = useDefensiveSetup()
syncFromDecision({
infield_depth: 'corners_in',
outfield_depth: 'shallow',
hold_runners: [1, 3],
})
expect(infieldDepth.value).toBe('corners_in')
expect(outfieldDepth.value).toBe('shallow')
expect(holdRunnersArray.value).toEqual([1, 3])
})
it('clears previously held runners not in new decision', () => {
const { toggleHold, syncFromDecision, isHeld } = useDefensiveSetup()
toggleHold(1)
toggleHold(2)
toggleHold(3)
syncFromDecision({
infield_depth: 'normal',
outfield_depth: 'normal',
hold_runners: [2],
})
expect(isHeld(1)).toBe(false)
expect(isHeld(2)).toBe(true)
expect(isHeld(3)).toBe(false)
})
})
describe('getDecision', () => {
it('returns a valid DefensiveDecision from current state', () => {
const { toggleHold, infieldDepth, getDecision } = useDefensiveSetup()
infieldDepth.value = 'infield_in'
toggleHold(1)
toggleHold(3)
const decision = getDecision()
expect(decision).toEqual({
infield_depth: 'infield_in',
outfield_depth: 'normal',
hold_runners: [1, 3],
})
})
it('returns defaults when nothing has been set', () => {
const { getDecision } = useDefensiveSetup()
expect(getDecision()).toEqual({
infield_depth: 'normal',
outfield_depth: 'normal',
hold_runners: [],
})
})
})
})