import { describe, it, expect, beforeEach, vi } from 'vitest' import { mount } from '@vue/test-utils' import PlayResult from '~/components/Gameplay/PlayResult.vue' import type { PlayResult as PlayResultType } from '~/types' describe('PlayResult', () => { const createPlayResult = (overrides = {}): PlayResultType => ({ play_number: 1, outcome: 'STRIKEOUT', description: 'Mike Trout strikes out swinging', outs_recorded: 1, runs_scored: 0, runners_advanced: [], batter_result: null, new_state: {}, is_hit: false, is_out: true, is_walk: false, is_strikeout: true, ...overrides, }) beforeEach(() => { vi.clearAllTimers() vi.useFakeTimers() }) // ============================================================================ // Rendering Tests // ============================================================================ describe('Rendering', () => { it('renders when result is provided', () => { const result = createPlayResult() const wrapper = mount(PlayResult, { props: { result }, }) expect(wrapper.find('.play-result').exists()).toBe(true) expect(wrapper.text()).toContain('Play #1') }) it('does not render when result is null', () => { const wrapper = mount(PlayResult, { props: { result: null }, }) expect(wrapper.find('.play-result').exists()).toBe(false) }) it('displays play description', () => { const result = createPlayResult({ description: 'Custom play description', }) const wrapper = mount(PlayResult, { props: { result }, }) expect(wrapper.text()).toContain('Custom play description') }) it('displays play number correctly', () => { const result = createPlayResult({ play_number: 42 }) const wrapper = mount(PlayResult, { props: { result }, }) expect(wrapper.text()).toContain('Play #42') }) }) // ============================================================================ // Result Type Styling Tests // ============================================================================ describe('Result Type Styling', () => { it('applies runs styling when runs scored', () => { const result = createPlayResult({ runs_scored: 2, is_out: false, }) const wrapper = mount(PlayResult, { props: { result }, }) expect(wrapper.find('.result-runs').exists()).toBe(true) }) it('applies hit styling when is_hit is true', () => { const result = createPlayResult({ is_hit: true, is_out: false, runs_scored: 0, }) const wrapper = mount(PlayResult, { props: { result }, }) expect(wrapper.find('.result-hit').exists()).toBe(true) }) it('applies out styling when is_out is true', () => { const result = createPlayResult({ is_out: true, is_hit: false, runs_scored: 0, }) const wrapper = mount(PlayResult, { props: { result }, }) expect(wrapper.find('.result-out').exists()).toBe(true) }) it('applies default styling when no special type', () => { const result = createPlayResult({ is_out: false, is_hit: false, runs_scored: 0, }) const wrapper = mount(PlayResult, { props: { result }, }) expect(wrapper.find('.result-default').exists()).toBe(true) }) it('prioritizes runs over hit styling', () => { const result = createPlayResult({ runs_scored: 1, is_hit: true, is_out: false, }) const wrapper = mount(PlayResult, { props: { result }, }) expect(wrapper.find('.result-runs').exists()).toBe(true) expect(wrapper.find('.result-hit').exists()).toBe(false) }) }) // ============================================================================ // Stats Display Tests // ============================================================================ describe('Stats Display', () => { it('displays outs recorded', () => { const result = createPlayResult({ outs_recorded: 2 }) const wrapper = mount(PlayResult, { props: { result }, }) expect(wrapper.find('.stat-outs').exists()).toBe(true) expect(wrapper.find('.stat-outs').text()).toContain('2') expect(wrapper.find('.stat-outs').text()).toContain('Outs') }) it('uses singular "Out" for single out', () => { const result = createPlayResult({ outs_recorded: 1 }) const wrapper = mount(PlayResult, { props: { result }, }) expect(wrapper.find('.stat-outs').text()).toContain('Out') expect(wrapper.find('.stat-outs').text()).not.toContain('Outs') }) it('displays runs scored', () => { const result = createPlayResult({ runs_scored: 3, is_out: false, }) const wrapper = mount(PlayResult, { props: { result }, }) expect(wrapper.find('.stat-runs').exists()).toBe(true) expect(wrapper.find('.stat-runs').text()).toContain('3') expect(wrapper.find('.stat-runs').text()).toContain('Runs') }) it('uses singular "Run" for single run', () => { const result = createPlayResult({ runs_scored: 1, is_out: false, }) const wrapper = mount(PlayResult, { props: { result }, }) expect(wrapper.find('.stat-runs').text()).toContain('Run') expect(wrapper.find('.stat-runs').text()).not.toContain('Runs') }) it('displays hit indicator when is_hit true', () => { const result = createPlayResult({ is_hit: true, is_out: false, }) const wrapper = mount(PlayResult, { props: { result }, }) expect(wrapper.find('.stat-hit').exists()).toBe(true) expect(wrapper.find('.stat-hit').text()).toContain('Hit') }) it('hides stats when values are zero', () => { const result = createPlayResult({ outs_recorded: 0, runs_scored: 0, is_hit: false, }) const wrapper = mount(PlayResult, { props: { result }, }) expect(wrapper.find('.stat-outs').exists()).toBe(false) expect(wrapper.find('.stat-runs').exists()).toBe(false) expect(wrapper.find('.stat-hit').exists()).toBe(false) }) }) // ============================================================================ // Runner Advancement Tests // ============================================================================ describe('Runner Advancement', () => { it('displays runner advancement section when runners advanced', () => { const result = createPlayResult({ runners_advanced: [ { from: 1, to: 3, out: false }, ], }) const wrapper = mount(PlayResult, { props: { result }, }) expect(wrapper.find('.runner-advancement').exists()).toBe(true) expect(wrapper.text()).toContain('Runner Movement:') }) it('hides runner advancement when no runners advanced', () => { const result = createPlayResult({ runners_advanced: [], }) const wrapper = mount(PlayResult, { props: { result }, }) expect(wrapper.find('.runner-advancement').exists()).toBe(false) }) it('formats base labels correctly', () => { const result = createPlayResult({ runners_advanced: [ { from: 0, to: 1 }, // Batter to 1st { from: 1, to: 2 }, // 1st to 2nd { from: 2, to: 3 }, // 2nd to 3rd { from: 3, to: 4 }, // 3rd to Home ], }) const wrapper = mount(PlayResult, { props: { result }, }) expect(wrapper.text()).toContain('Batter') expect(wrapper.text()).toContain('1st') expect(wrapper.text()).toContain('2nd') expect(wrapper.text()).toContain('3rd') expect(wrapper.text()).toContain('Home') }) it('shows "Out" for runners who are out', () => { const result = createPlayResult({ runners_advanced: [ { from: 1, to: 2, out: true }, ], }) const wrapper = mount(PlayResult, { props: { result }, }) expect(wrapper.find('.advancement-out').exists()).toBe(true) expect(wrapper.text()).toContain('Out') }) it('displays multiple runner advancements', () => { const result = createPlayResult({ runners_advanced: [ { from: 1, to: 3 }, { from: 0, to: 1 }, ], }) const wrapper = mount(PlayResult, { props: { result }, }) const items = wrapper.findAll('.advancement-item') expect(items).toHaveLength(2) }) }) // ============================================================================ // Dismiss Button Tests // ============================================================================ describe('Dismiss Button', () => { it('shows dismiss button when autoHide is false', () => { const result = createPlayResult() const wrapper = mount(PlayResult, { props: { result, autoHide: false, }, }) expect(wrapper.find('.dismiss-button').exists()).toBe(true) }) it('hides dismiss button when autoHide is true', () => { const result = createPlayResult() const wrapper = mount(PlayResult, { props: { result, autoHide: true, }, }) expect(wrapper.find('.dismiss-button').exists()).toBe(false) }) it('emits dismiss event when button clicked', async () => { const result = createPlayResult() const wrapper = mount(PlayResult, { props: { result, autoHide: false, }, }) await wrapper.find('.dismiss-button').trigger('click') expect(wrapper.emitted('dismiss')).toBeTruthy() expect(wrapper.emitted('dismiss')).toHaveLength(1) }) }) // ============================================================================ // Auto-Hide Tests // ============================================================================ describe('Auto-Hide', () => { it('emits dismiss after 5 seconds when autoHide is true', async () => { const result = createPlayResult() const wrapper = mount(PlayResult, { props: { result, autoHide: true, }, }) expect(wrapper.emitted('dismiss')).toBeFalsy() vi.advanceTimersByTime(5000) await wrapper.vm.$nextTick() expect(wrapper.emitted('dismiss')).toBeTruthy() expect(wrapper.emitted('dismiss')).toHaveLength(1) }) it('does not auto-dismiss when autoHide is false', async () => { const result = createPlayResult() const wrapper = mount(PlayResult, { props: { result, autoHide: false, }, }) vi.advanceTimersByTime(10000) await wrapper.vm.$nextTick() expect(wrapper.emitted('dismiss')).toBeFalsy() }) it('does not auto-dismiss when result is null', async () => { const wrapper = mount(PlayResult, { props: { result: null, autoHide: true, }, }) vi.advanceTimersByTime(5000) await wrapper.vm.$nextTick() expect(wrapper.emitted('dismiss')).toBeFalsy() }) }) // ============================================================================ // Edge Cases // ============================================================================ describe('Edge Cases', () => { it('handles very long play descriptions', () => { const result = createPlayResult({ description: 'A'.repeat(200), }) const wrapper = mount(PlayResult, { props: { result }, }) expect(wrapper.find('.result-description').text()).toHaveLength(200) }) it('handles high play numbers', () => { const result = createPlayResult({ play_number: 999 }) const wrapper = mount(PlayResult, { props: { result }, }) expect(wrapper.text()).toContain('Play #999') }) it('handles multiple outs recorded', () => { const result = createPlayResult({ outs_recorded: 3 }) const wrapper = mount(PlayResult, { props: { result }, }) expect(wrapper.find('.stat-outs').text()).toContain('3') }) it('handles grand slam scenario (4 runs)', () => { const result = createPlayResult({ runs_scored: 4, is_hit: true, outs_recorded: 0, }) const wrapper = mount(PlayResult, { props: { result }, }) expect(wrapper.find('.stat-runs').text()).toContain('4') expect(wrapper.find('.result-runs').exists()).toBe(true) }) }) })