strat-gameplay-webapp/frontend-sba/tests/unit/components/Decisions/DefensiveSetup.spec.ts
Cal Corum 4e7ea9e514 CLAUDE: Remove alignment field from frontend - complete Session 1 cleanup
Removed all references to the defensive alignment field across frontend codebase
after backend removal in Session 1. The alignment field was determined to be unused
and was removed from DefensiveDecision model.

Changes:
- types/websocket.ts: Removed alignment from DefensiveDecisionRequest interface
- composables/useGameActions.ts: Removed alignment from submit handler
- pages/demo-decisions.vue: Updated demo state and summary text (alignment → depths)
- pages/games/[id].vue: Updated decision history text for both defensive and offensive
  * Defensive: Now shows "infield depth, outfield depth" instead of "alignment, infield"
  * Offensive: Updated to use new action field with proper labels (swing_away, hit_and_run, etc.)
- Test files (3): Updated all test cases to remove alignment references
  * tests/unit/composables/useGameActions.spec.ts
  * tests/unit/store/game-decisions.spec.ts
  * tests/unit/components/Decisions/DefensiveSetup.spec.ts

Also updated offensive decision handling to match Session 2 changes (approach/hit_and_run/bunt_attempt → action field).

Total: 7 files modified, all alignment references removed
Verified: Zero remaining alignment references in .ts/.vue/.js files

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-14 15:34:59 -06:00

330 lines
8.8 KiB
TypeScript

import { describe, it, expect, beforeEach } from 'vitest'
import { mount } from '@vue/test-utils'
import DefensiveSetup from '~/components/Decisions/DefensiveSetup.vue'
import type { DefensiveDecision } from '~/types/game'
describe('DefensiveSetup', () => {
const defaultProps = {
gameId: 'test-game-123',
isActive: true,
}
describe('Rendering', () => {
it('renders component with header', () => {
const wrapper = mount(DefensiveSetup, {
props: defaultProps,
})
expect(wrapper.text()).toContain('Defensive Setup')
})
it('shows opponent turn indicator when not active', () => {
const wrapper = mount(DefensiveSetup, {
props: {
...defaultProps,
isActive: false,
},
})
expect(wrapper.text()).toContain("Opponent's Turn")
})
it('renders all form sections', () => {
const wrapper = mount(DefensiveSetup, {
props: defaultProps,
})
expect(wrapper.text()).toContain('Infield Depth')
expect(wrapper.text()).toContain('Outfield Depth')
expect(wrapper.text()).toContain('Hold Runners')
})
})
describe('Initial Values', () => {
it('uses default values when no currentSetup provided', () => {
const wrapper = mount(DefensiveSetup, {
props: defaultProps,
})
// Check preview shows defaults
expect(wrapper.text()).toContain('Normal')
})
it('uses provided currentSetup values', () => {
const currentSetup: DefensiveDecision = {
infield_depth: 'back',
outfield_depth: 'normal',
hold_runners: [1, 3],
}
const wrapper = mount(DefensiveSetup, {
props: {
...defaultProps,
currentSetup,
},
})
expect(wrapper.vm.localSetup.infield_depth).toBe('back')
expect(wrapper.vm.localSetup.outfield_depth).toBe('normal')
expect(wrapper.vm.localSetup.hold_runners).toEqual([1, 3])
})
})
describe('Hold Runners', () => {
it('initializes hold runner toggles from currentSetup', () => {
const wrapper = mount(DefensiveSetup, {
props: {
...defaultProps,
currentSetup: {
infield_depth: 'normal',
outfield_depth: 'normal',
hold_runners: [1, 2],
},
},
})
expect(wrapper.vm.holdFirst).toBe(true)
expect(wrapper.vm.holdSecond).toBe(true)
expect(wrapper.vm.holdThird).toBe(false)
})
it('updates hold_runners array when toggles change', async () => {
const wrapper = mount(DefensiveSetup, {
props: defaultProps,
})
wrapper.vm.holdFirst = true
wrapper.vm.holdThird = true
await wrapper.vm.$nextTick()
expect(wrapper.vm.localSetup.hold_runners).toContain(1)
expect(wrapper.vm.localSetup.hold_runners).toContain(3)
expect(wrapper.vm.localSetup.hold_runners).not.toContain(2)
})
})
describe('Preview Display', () => {
it('displays current infield depth in preview', () => {
const wrapper = mount(DefensiveSetup, {
props: {
...defaultProps,
currentSetup: {
infield_depth: 'back',
outfield_depth: 'normal',
hold_runners: [],
},
},
})
expect(wrapper.text()).toContain('Back')
})
it('displays holding status for multiple runners', () => {
const wrapper = mount(DefensiveSetup, {
props: {
...defaultProps,
currentSetup: {
infield_depth: 'normal',
outfield_depth: 'normal',
hold_runners: [1, 2, 3],
},
},
})
const holdingText = wrapper.vm.holdingDisplay
expect(holdingText).toContain('1st')
expect(holdingText).toContain('2nd')
expect(holdingText).toContain('3rd')
})
it('shows "None" when no runners held', () => {
const wrapper = mount(DefensiveSetup, {
props: defaultProps,
})
expect(wrapper.vm.holdingDisplay).toBe('None')
})
})
describe('Form Submission', () => {
it('emits submit event with current setup', async () => {
const wrapper = mount(DefensiveSetup, {
props: defaultProps,
})
wrapper.vm.localSetup = {
infield_depth: 'in',
outfield_depth: 'normal',
hold_runners: [2],
}
await wrapper.find('form').trigger('submit.prevent')
expect(wrapper.emitted('submit')).toBeTruthy()
const emitted = wrapper.emitted('submit')![0][0] as DefensiveDecision
expect(emitted.infield_depth).toBe('in')
expect(emitted.outfield_depth).toBe('normal')
expect(emitted.hold_runners).toEqual([2])
})
it('does not submit when not active', async () => {
const wrapper = mount(DefensiveSetup, {
props: {
...defaultProps,
isActive: false,
},
})
await wrapper.find('form').trigger('submit.prevent')
expect(wrapper.emitted('submit')).toBeFalsy()
})
it('does not submit when no changes', async () => {
const currentSetup: DefensiveDecision = {
infield_depth: 'normal',
outfield_depth: 'normal',
hold_runners: [],
}
const wrapper = mount(DefensiveSetup, {
props: {
...defaultProps,
currentSetup,
},
})
await wrapper.find('form').trigger('submit.prevent')
expect(wrapper.emitted('submit')).toBeFalsy()
})
it('shows loading state during submission', async () => {
const wrapper = mount(DefensiveSetup, {
props: defaultProps,
})
// Trigger submission
wrapper.vm.submitting = true
await wrapper.vm.$nextTick()
// Verify button is in loading state
expect(wrapper.vm.submitting).toBe(true)
})
})
describe('Submit Button State', () => {
it('shows "Wait for Your Turn" when not active', () => {
const wrapper = mount(DefensiveSetup, {
props: {
...defaultProps,
isActive: false,
},
})
expect(wrapper.vm.submitButtonText).toBe('Wait for Your Turn')
})
it('shows "No Changes" when setup unchanged', () => {
const currentSetup: DefensiveDecision = {
infield_depth: 'normal',
outfield_depth: 'normal',
hold_runners: [],
}
const wrapper = mount(DefensiveSetup, {
props: {
...defaultProps,
currentSetup,
},
})
expect(wrapper.vm.submitButtonText).toBe('No Changes')
})
it('shows "Submit Defensive Setup" when active with changes', () => {
const wrapper = mount(DefensiveSetup, {
props: defaultProps,
})
wrapper.vm.localSetup.infield_depth = 'back'
expect(wrapper.vm.submitButtonText).toBe('Submit Defensive Setup')
})
})
describe('Change Detection', () => {
it('detects infield depth changes', () => {
const wrapper = mount(DefensiveSetup, {
props: {
...defaultProps,
currentSetup: {
infield_depth: 'normal',
outfield_depth: 'normal',
hold_runners: [],
},
},
})
expect(wrapper.vm.hasChanges).toBe(false)
wrapper.vm.localSetup.infield_depth = 'back'
expect(wrapper.vm.hasChanges).toBe(true)
})
it('detects hold runners changes', () => {
const wrapper = mount(DefensiveSetup, {
props: {
...defaultProps,
currentSetup: {
infield_depth: 'normal',
outfield_depth: 'normal',
hold_runners: [],
},
},
})
expect(wrapper.vm.hasChanges).toBe(false)
wrapper.vm.localSetup.hold_runners = [1]
expect(wrapper.vm.hasChanges).toBe(true)
})
})
describe('Prop Updates', () => {
it('updates local state when currentSetup prop changes', async () => {
const wrapper = mount(DefensiveSetup, {
props: defaultProps,
})
const newSetup: DefensiveDecision = {
infield_depth: 'double_play',
outfield_depth: 'normal',
hold_runners: [1, 2, 3],
}
await wrapper.setProps({ currentSetup: newSetup })
expect(wrapper.vm.localSetup.infield_depth).toBe('double_play')
expect(wrapper.vm.localSetup.outfield_depth).toBe('normal')
expect(wrapper.vm.localSetup.hold_runners).toEqual([1, 2, 3])
})
})
describe('Disabled State', () => {
it('disables all controls when not active', () => {
const wrapper = mount(DefensiveSetup, {
props: {
...defaultProps,
isActive: false,
},
})
const buttonGroups = wrapper.findAllComponents({ name: 'ButtonGroup' })
buttonGroups.forEach(bg => {
expect(bg.props('disabled')).toBe(true)
})
const toggles = wrapper.findAllComponents({ name: 'ToggleSwitch' })
toggles.forEach(toggle => {
expect(toggle.props('disabled')).toBe(true)
})
})
})
})