import { describe, it, expect } from 'vitest' import { mount } from '@vue/test-utils' import StolenBaseInputs from '~/components/Decisions/StolenBaseInputs.vue' describe('StolenBaseInputs', () => { const defaultProps = { runners: { first: null, second: null, third: null, }, isActive: true, } describe('Rendering', () => { it('renders component with header', () => { const wrapper = mount(StolenBaseInputs, { props: defaultProps, }) expect(wrapper.text()).toContain('Stolen Base Attempts') }) it('shows empty state when no runners', () => { const wrapper = mount(StolenBaseInputs, { props: defaultProps, }) expect(wrapper.text()).toContain('No runners on base') }) it('shows toggles when runners on base', () => { const wrapper = mount(StolenBaseInputs, { props: { ...defaultProps, runners: { first: 101, second: null, third: null, }, }, }) expect(wrapper.text()).toContain('Runner on 1st attempts steal') }) it('renders mini diamond visualization', () => { const wrapper = mount(StolenBaseInputs, { props: { ...defaultProps, runners: { first: 101, second: 102, third: null, }, }, }) // Check for base elements in the diamond const bases = wrapper.findAll('.w-5.h-5.rounded-full') expect(bases.length).toBeGreaterThan(0) }) }) describe('Runner Detection', () => { it('detects no runners', () => { const wrapper = mount(StolenBaseInputs, { props: defaultProps, }) expect(wrapper.vm.hasRunners).toBe(false) }) it('detects runner on first', () => { const wrapper = mount(StolenBaseInputs, { props: { ...defaultProps, runners: { first: 101, second: null, third: null, }, }, }) expect(wrapper.vm.hasRunners).toBe(true) }) it('detects multiple runners', () => { const wrapper = mount(StolenBaseInputs, { props: { ...defaultProps, runners: { first: 101, second: 102, third: 103, }, }, }) expect(wrapper.vm.hasRunners).toBe(true) }) }) describe('Steal Toggles', () => { it('shows toggle for runner on first', () => { const wrapper = mount(StolenBaseInputs, { props: { ...defaultProps, runners: { first: 101, second: null, third: null, }, }, }) expect(wrapper.text()).toContain('Runner on 1st attempts steal to 2nd') }) it('shows toggle for runner on second', () => { const wrapper = mount(StolenBaseInputs, { props: { ...defaultProps, runners: { first: null, second: 102, third: null, }, }, }) expect(wrapper.text()).toContain('Runner on 2nd attempts steal to 3rd') }) it('shows toggle for runner on third', () => { const wrapper = mount(StolenBaseInputs, { props: { ...defaultProps, runners: { first: null, second: null, third: 103, }, }, }) expect(wrapper.text()).toContain('Runner on 3rd attempts steal home') }) it('shows all toggles when bases loaded', () => { const wrapper = mount(StolenBaseInputs, { props: { ...defaultProps, runners: { first: 101, second: 102, third: 103, }, }, }) expect(wrapper.text()).toContain('Runner on 1st') expect(wrapper.text()).toContain('Runner on 2nd') expect(wrapper.text()).toContain('Runner on 3rd') }) }) describe('Steal Attempts Calculation', () => { it('calculates empty steal attempts by default', () => { const wrapper = mount(StolenBaseInputs, { props: { ...defaultProps, runners: { first: 101, second: null, third: null, }, }, }) expect(wrapper.vm.stealAttempts).toEqual([]) }) it('calculates steal to second when first base toggle is on', async () => { const wrapper = mount(StolenBaseInputs, { props: { ...defaultProps, runners: { first: 101, second: null, third: null, }, }, }) wrapper.vm.stealToSecond = true await wrapper.vm.$nextTick() expect(wrapper.vm.stealAttempts).toContain(2) }) it('calculates steal to third when second base toggle is on', async () => { const wrapper = mount(StolenBaseInputs, { props: { ...defaultProps, runners: { first: null, second: 102, third: null, }, }, }) wrapper.vm.stealToThird = true await wrapper.vm.$nextTick() expect(wrapper.vm.stealAttempts).toContain(3) }) it('calculates steal home when third base toggle is on', async () => { const wrapper = mount(StolenBaseInputs, { props: { ...defaultProps, runners: { first: null, second: null, third: 103, }, }, }) wrapper.vm.stealHome = true await wrapper.vm.$nextTick() expect(wrapper.vm.stealAttempts).toContain(4) }) it('calculates multiple steal attempts', async () => { const wrapper = mount(StolenBaseInputs, { props: { ...defaultProps, runners: { first: 101, second: 102, third: 103, }, }, }) wrapper.vm.stealToSecond = true wrapper.vm.stealHome = true await wrapper.vm.$nextTick() expect(wrapper.vm.stealAttempts).toContain(2) expect(wrapper.vm.stealAttempts).toContain(4) expect(wrapper.vm.stealAttempts.length).toBe(2) }) it('does not include steal if no runner on that base', async () => { const wrapper = mount(StolenBaseInputs, { props: { ...defaultProps, runners: { first: null, second: 102, third: null, }, }, }) // Try to enable steal from first (no runner) wrapper.vm.stealToSecond = true await wrapper.vm.$nextTick() expect(wrapper.vm.stealAttempts).not.toContain(2) }) }) describe('Change Detection', () => { it('detects no changes when attempts match currentAttempts', () => { const wrapper = mount(StolenBaseInputs, { props: { ...defaultProps, runners: { first: 101, second: null, third: null, }, currentAttempts: [], }, }) expect(wrapper.vm.hasChanges).toBe(false) }) it('detects changes when attempts differ', async () => { const wrapper = mount(StolenBaseInputs, { props: { ...defaultProps, runners: { first: 101, second: null, third: null, }, currentAttempts: [], }, }) wrapper.vm.stealToSecond = true await wrapper.vm.$nextTick() expect(wrapper.vm.hasChanges).toBe(true) }) }) describe('Submit and Cancel', () => { it('emits submit with steal attempts', async () => { const wrapper = mount(StolenBaseInputs, { props: { ...defaultProps, runners: { first: 101, second: 102, third: null, }, }, }) wrapper.vm.stealToSecond = true wrapper.vm.stealToThird = true await wrapper.vm.$nextTick() await wrapper.vm.handleSubmit() expect(wrapper.emitted('submit')).toBeTruthy() expect(wrapper.emitted('submit')![0]).toEqual([[2, 3]]) }) it('does not submit when not active', async () => { const wrapper = mount(StolenBaseInputs, { props: { ...defaultProps, isActive: false, runners: { first: 101, second: null, third: null, }, }, }) await wrapper.vm.handleSubmit() expect(wrapper.emitted('submit')).toBeFalsy() }) it('does not submit when no changes', async () => { const wrapper = mount(StolenBaseInputs, { props: { ...defaultProps, runners: { first: 101, second: null, third: null, }, currentAttempts: [], }, }) await wrapper.vm.handleSubmit() expect(wrapper.emitted('submit')).toBeFalsy() }) it('emits cancel event', () => { const wrapper = mount(StolenBaseInputs, { props: { ...defaultProps, runners: { first: 101, second: null, third: null, }, }, }) wrapper.vm.handleCancel() expect(wrapper.emitted('cancel')).toBeTruthy() }) it('resets toggles on cancel', () => { const wrapper = mount(StolenBaseInputs, { props: { ...defaultProps, runners: { first: 101, second: null, third: null, }, currentAttempts: [2], }, }) // Change toggle wrapper.vm.stealToSecond = false // Cancel should reset wrapper.vm.handleCancel() expect(wrapper.vm.stealToSecond).toBe(true) }) }) describe('Submit Button Text', () => { it('shows wait message when not active', () => { const wrapper = mount(StolenBaseInputs, { props: { ...defaultProps, isActive: false, runners: { first: 101, second: null, third: null, }, }, }) expect(wrapper.vm.submitButtonText).toBe('Wait for Your Turn') }) it('shows no changes message', () => { const wrapper = mount(StolenBaseInputs, { props: { ...defaultProps, runners: { first: 101, second: null, third: null, }, currentAttempts: [], }, }) expect(wrapper.vm.submitButtonText).toBe('No Changes') }) it('shows no attempts message when no steals selected', async () => { const wrapper = mount(StolenBaseInputs, { props: { ...defaultProps, runners: { first: 101, second: null, third: null, }, currentAttempts: [2], }, }) wrapper.vm.stealToSecond = false await wrapper.vm.$nextTick() expect(wrapper.vm.submitButtonText).toBe('No Steal Attempts') }) it('shows count of attempts', async () => { const wrapper = mount(StolenBaseInputs, { props: { ...defaultProps, runners: { first: 101, second: 102, third: null, }, }, }) wrapper.vm.stealToSecond = true wrapper.vm.stealToThird = true await wrapper.vm.$nextTick() expect(wrapper.vm.submitButtonText).toBe('Submit 2 Attempts') }) }) describe('Runner Changes', () => { it('clears steal toggle when runner removed from base', async () => { const wrapper = mount(StolenBaseInputs, { props: { ...defaultProps, runners: { first: 101, second: null, third: null, }, }, }) wrapper.vm.stealToSecond = true await wrapper.vm.$nextTick() // Remove runner from first await wrapper.setProps({ runners: { first: null, second: null, third: null, }, }) expect(wrapper.vm.stealToSecond).toBe(false) }) }) })