diff --git a/frontend-sba/tests/unit/components/Game/RunnerCard.spec.ts b/frontend-sba/tests/unit/components/Game/RunnerCard.spec.ts new file mode 100644 index 0000000..8ae8215 --- /dev/null +++ b/frontend-sba/tests/unit/components/Game/RunnerCard.spec.ts @@ -0,0 +1,606 @@ +import { describe, it, expect, beforeEach } from "vitest"; +import { mount } from "@vue/test-utils"; +import { createPinia, setActivePinia } from "pinia"; +import RunnerCard from "~/components/Game/RunnerCard.vue"; +import { useGameStore } from "~/store/game"; +import type { LineupPlayerState } from "~/types/game"; + +describe("RunnerCard", () => { + let pinia: ReturnType; + + beforeEach(() => { + pinia = createPinia(); + setActivePinia(pinia); + }); + + const mockRunner: LineupPlayerState = { + lineup_id: 1, + batting_order: 1, + position: "LF", + card_id: 101, + }; + + describe("empty base state", () => { + it("renders empty state when no runner provided", () => { + const wrapper = mount(RunnerCard, { + global: { plugins: [pinia] }, + props: { + base: "1B", + runner: null, + isExpanded: false, + teamColor: "#3b82f6", + }, + }); + + expect(wrapper.find(".runner-card.empty").exists()).toBe(true); + expect(wrapper.text()).toContain("Empty"); + }); + + it("displays base label for empty base", () => { + const wrapper = mount(RunnerCard, { + global: { plugins: [pinia] }, + props: { + base: "2B", + runner: null, + isExpanded: false, + teamColor: "#3b82f6", + }, + }); + + expect(wrapper.text()).toContain("2B"); + }); + + it("shows hollow circle for empty base", () => { + const wrapper = mount(RunnerCard, { + global: { plugins: [pinia] }, + props: { + base: "3B", + runner: null, + isExpanded: false, + teamColor: "#3b82f6", + }, + }); + + const circle = wrapper.find(".rounded-full.border-dashed"); + expect(circle.exists()).toBe(true); + }); + + it("does not emit click event for empty base", async () => { + const wrapper = mount(RunnerCard, { + global: { plugins: [pinia] }, + props: { + base: "1B", + runner: null, + isExpanded: false, + teamColor: "#3b82f6", + }, + }); + + await wrapper.trigger("click"); + expect(wrapper.emitted("click")).toBeUndefined(); + }); + }); + + describe("occupied base state", () => { + beforeEach(() => { + const gameStore = useGameStore(); + // Set game state first so updateLineup knows the team ID + gameStore.setGameState({ + id: 1, + home_team_id: 1, + away_team_id: 2, + status: "active", + inning: 1, + half: "top", + outs: 0, + home_score: 0, + away_score: 0, + home_team_abbrev: "NYY", + away_team_abbrev: "BOS", + home_team_dice_color: "3b82f6", + current_batter: null, + current_pitcher: null, + on_first: null, + on_second: null, + on_third: null, + decision_phase: "idle", + play_count: 0, + }); + + gameStore.updateLineup(1, [ + { + id: 1, + lineup_id: 1, + team_id: 1, + batting_order: 1, + position: "LF", + is_active: true, + player: { + id: 101, + name: "Mike Trout", + image: "https://example.com/trout.jpg", + headshot: "https://example.com/trout-headshot.jpg", + }, + }, + ]); + }); + + it("renders occupied state when runner provided", () => { + const wrapper = mount(RunnerCard, { + global: { plugins: [pinia] }, + props: { + base: "1B", + runner: mockRunner, + isExpanded: false, + teamColor: "#3b82f6", + }, + }); + + expect(wrapper.find(".runner-card.occupied").exists()).toBe(true); + expect(wrapper.find(".runner-card.empty").exists()).toBe(false); + }); + + it("displays runner name", () => { + const wrapper = mount(RunnerCard, { + global: { plugins: [pinia] }, + props: { + base: "1B", + runner: mockRunner, + isExpanded: false, + teamColor: "#3b82f6", + }, + }); + + expect(wrapper.text()).toContain("Mike Trout"); + }); + + it("displays base label", () => { + const wrapper = mount(RunnerCard, { + global: { plugins: [pinia] }, + props: { + base: "2B", + runner: mockRunner, + isExpanded: false, + teamColor: "#3b82f6", + }, + }); + + expect(wrapper.text()).toContain("2B"); + }); + + it("displays runner number based on lineup_id", () => { + const wrapper = mount(RunnerCard, { + global: { plugins: [pinia] }, + props: { + base: "1B", + runner: mockRunner, + isExpanded: false, + teamColor: "#3b82f6", + }, + }); + + expect(wrapper.text()).toContain("#01"); + }); + + it("displays player headshot when available", () => { + const wrapper = mount(RunnerCard, { + global: { plugins: [pinia] }, + props: { + base: "1B", + runner: mockRunner, + isExpanded: false, + teamColor: "#3b82f6", + }, + }); + + const img = wrapper.find('img[alt="Mike Trout"]'); + expect(img.exists()).toBe(true); + expect(img.attributes("src")).toBe( + "https://example.com/trout-headshot.jpg", + ); + }); + + it("applies team color to border", () => { + const wrapper = mount(RunnerCard, { + global: { plugins: [pinia] }, + props: { + base: "1B", + runner: mockRunner, + isExpanded: false, + teamColor: "#ff0000", + }, + }); + + const avatar = wrapper.find(".rounded-full.border-2"); + expect(avatar.attributes("style")).toContain( + "border-color: #ff0000", + ); + }); + + it("shows chevron icon when occupied", () => { + const wrapper = mount(RunnerCard, { + global: { plugins: [pinia] }, + props: { + base: "1B", + runner: mockRunner, + isExpanded: false, + teamColor: "#3b82f6", + }, + }); + + const chevron = wrapper.find("svg"); + expect(chevron.exists()).toBe(true); + }); + + it("emits click event when clicked", async () => { + const wrapper = mount(RunnerCard, { + global: { plugins: [pinia] }, + props: { + base: "1B", + runner: mockRunner, + isExpanded: false, + teamColor: "#3b82f6", + }, + }); + + await wrapper.trigger("click"); + expect(wrapper.emitted("click")).toHaveLength(1); + }); + }); + + describe("expanded state", () => { + beforeEach(() => { + const gameStore = useGameStore(); + gameStore.setGameState({ + id: 1, + home_team_id: 1, + away_team_id: 2, + status: "active", + inning: 1, + half: "top", + outs: 0, + on_base_code: 0, + home_team: { + id: 1, + name: "Home Team", + abbreviation: "HOME", + dice_color: "3b82f6", + }, + away_team: { + id: 2, + name: "Away Team", + abbreviation: "AWAY", + dice_color: "10b981", + }, + }); + gameStore.updateLineup(1, [ + { + id: 1, + lineup_id: 1, + team_id: 1, + batting_order: 1, + position: "LF", + is_active: true, + player: { + id: 101, + name: "Mike Trout", + image: "https://example.com/trout-card.jpg", + headshot: "https://example.com/trout-headshot.jpg", + }, + }, + ]); + }); + + it("does not show expanded view when collapsed", () => { + const wrapper = mount(RunnerCard, { + global: { plugins: [pinia] }, + props: { + base: "1B", + runner: mockRunner, + isExpanded: false, + teamColor: "#3b82f6", + }, + }); + + expect(wrapper.find(".runner-expanded").exists()).toBe(false); + }); + + it("shows expanded view when isExpanded is true", () => { + const wrapper = mount(RunnerCard, { + global: { plugins: [pinia] }, + props: { + base: "1B", + runner: mockRunner, + isExpanded: true, + teamColor: "#3b82f6", + }, + }); + + expect(wrapper.find(".runner-expanded").exists()).toBe(true); + }); + + it("displays full player card image when expanded", () => { + const wrapper = mount(RunnerCard, { + global: { plugins: [pinia] }, + props: { + base: "1B", + runner: mockRunner, + isExpanded: true, + teamColor: "#3b82f6", + }, + }); + + const cardImg = wrapper.find( + '.runner-expanded img[alt="Mike Trout card"]', + ); + expect(cardImg.exists()).toBe(true); + expect(cardImg.attributes("src")).toBe( + "https://example.com/trout-card.jpg", + ); + }); + + it("shows player initials when no card image available", () => { + const gameStore = useGameStore(); + gameStore.setGameState({ + id: 1, + home_team_id: 1, + away_team_id: 2, + status: "active", + inning: 1, + half: "top", + outs: 0, + on_base_code: 0, + home_team: { + id: 1, + name: "Home Team", + abbreviation: "HOME", + dice_color: "3b82f6", + }, + away_team: { + id: 2, + name: "Away Team", + abbreviation: "AWAY", + dice_color: "10b981", + }, + }); + gameStore.updateLineup(1, [ + { + id: 1, + lineup_id: 1, + team_id: 1, + batting_order: 1, + position: "LF", + is_active: true, + player: { + id: 101, + name: "Mike Trout", + image: "", + headshot: "", + }, + }, + ]); + + const wrapper = mount(RunnerCard, { + global: { plugins: [pinia] }, + props: { + base: "1B", + runner: mockRunner, + isExpanded: true, + teamColor: "#3b82f6", + }, + }); + + expect(wrapper.text()).toContain("MT"); + }); + + it('displays "RUNNER" label in expanded header', () => { + const wrapper = mount(RunnerCard, { + global: { plugins: [pinia] }, + props: { + base: "2B", + runner: mockRunner, + isExpanded: true, + teamColor: "#3b82f6", + }, + }); + + expect(wrapper.find(".runner-expanded").text()).toContain("RUNNER"); + }); + + it("applies expanded class when isExpanded is true", () => { + const wrapper = mount(RunnerCard, { + global: { plugins: [pinia] }, + props: { + base: "1B", + runner: mockRunner, + isExpanded: true, + teamColor: "#3b82f6", + }, + }); + + expect(wrapper.find(".runner-card.expanded").exists()).toBe(true); + }); + + it("rotates chevron when expanded", () => { + const wrapper = mount(RunnerCard, { + global: { plugins: [pinia] }, + props: { + base: "1B", + runner: mockRunner, + isExpanded: true, + teamColor: "#3b82f6", + }, + }); + + const chevron = wrapper.find("svg"); + expect(chevron.classes()).toContain("rotate-90"); + }); + }); + + describe("player name handling", () => { + it('shows "Unknown Runner" when player not found in store', () => { + const wrapper = mount(RunnerCard, { + global: { plugins: [pinia] }, + props: { + base: "1B", + runner: mockRunner, + isExpanded: false, + teamColor: "#3b82f6", + }, + }); + + expect(wrapper.text()).toContain("Unknown Runner"); + }); + + it("extracts initials from first and last name", () => { + const gameStore = useGameStore(); + gameStore.setGameState({ + id: 1, + home_team_id: 1, + away_team_id: 2, + status: "active", + inning: 1, + half: "top", + outs: 0, + on_base_code: 0, + home_team: { + id: 1, + name: "Home Team", + abbreviation: "HOME", + dice_color: "3b82f6", + }, + away_team: { + id: 2, + name: "Away Team", + abbreviation: "AWAY", + dice_color: "10b981", + }, + }); + gameStore.updateLineup(1, [ + { + id: 1, + lineup_id: 1, + team_id: 1, + batting_order: 1, + position: "LF", + is_active: true, + player: { + id: 101, + name: "Aaron Donald Judge", + image: "", + }, + }, + ]); + + const wrapper = mount(RunnerCard, { + global: { plugins: [pinia] }, + props: { + base: "1B", + runner: mockRunner, + isExpanded: true, + teamColor: "#3b82f6", + }, + }); + + // Should use first and last name (A + J) + expect(wrapper.text()).toContain("AJ"); + }); + + it("handles single-word names", () => { + const gameStore = useGameStore(); + gameStore.setGameState({ + id: 1, + home_team_id: 1, + away_team_id: 2, + status: "active", + inning: 1, + half: "top", + outs: 0, + on_base_code: 0, + home_team: { + id: 1, + name: "Home Team", + abbreviation: "HOME", + dice_color: "3b82f6", + }, + away_team: { + id: 2, + name: "Away Team", + abbreviation: "AWAY", + dice_color: "10b981", + }, + }); + gameStore.updateLineup(1, [ + { + id: 1, + lineup_id: 1, + team_id: 1, + batting_order: 1, + position: "LF", + is_active: true, + player: { + id: 101, + name: "Pele", + image: "", + }, + }, + ]); + + const wrapper = mount(RunnerCard, { + global: { plugins: [pinia] }, + props: { + base: "1B", + runner: mockRunner, + isExpanded: true, + teamColor: "#3b82f6", + }, + }); + + expect(wrapper.text()).toContain("PE"); + }); + }); + + describe("base label variations", () => { + it("displays 1B correctly", () => { + const wrapper = mount(RunnerCard, { + global: { plugins: [pinia] }, + props: { + base: "1B", + runner: null, + isExpanded: false, + teamColor: "#3b82f6", + }, + }); + + expect(wrapper.text()).toContain("1B"); + }); + + it("displays 2B correctly", () => { + const wrapper = mount(RunnerCard, { + global: { plugins: [pinia] }, + props: { + base: "2B", + runner: null, + isExpanded: false, + teamColor: "#3b82f6", + }, + }); + + expect(wrapper.text()).toContain("2B"); + }); + + it("displays 3B correctly", () => { + const wrapper = mount(RunnerCard, { + global: { plugins: [pinia] }, + props: { + base: "3B", + runner: null, + isExpanded: false, + teamColor: "#3b82f6", + }, + }); + + expect(wrapper.text()).toContain("3B"); + }); + }); +}); diff --git a/frontend-sba/tests/unit/components/Game/RunnersOnBase.spec.ts b/frontend-sba/tests/unit/components/Game/RunnersOnBase.spec.ts new file mode 100644 index 0000000..28133f7 --- /dev/null +++ b/frontend-sba/tests/unit/components/Game/RunnersOnBase.spec.ts @@ -0,0 +1,577 @@ +import { describe, it, expect, beforeEach, vi } from "vitest"; +import { mount } from "@vue/test-utils"; +import { createPinia, setActivePinia } from "pinia"; +import RunnersOnBase from "~/components/Game/RunnersOnBase.vue"; +import RunnerCard from "~/components/Game/RunnerCard.vue"; +import { useGameStore } from "~/store/game"; +import type { LineupPlayerState, Lineup } from "~/types/game"; + +describe("RunnersOnBase", () => { + let pinia: ReturnType; + + beforeEach(() => { + pinia = createPinia(); + setActivePinia(pinia); + }); + + const mockRunnerFirst: LineupPlayerState = { + lineup_id: 1, + batting_order: 1, + position: "LF", + card_id: 101, + }; + + const mockRunnerSecond: LineupPlayerState = { + lineup_id: 2, + batting_order: 2, + position: "CF", + card_id: 102, + }; + + const mockRunnerThird: LineupPlayerState = { + lineup_id: 3, + batting_order: 3, + position: "RF", + card_id: 103, + }; + + const mockCatcher: Lineup = { + id: 1, + lineup_id: 4, + team_id: 1, + batting_order: 4, + position: "C", + is_active: true, + player: { + id: 104, + name: "Buster Posey", + image: "https://example.com/posey.jpg", + headshot: "https://example.com/posey-headshot.jpg", + }, + }; + + const mockFieldingLineup: Lineup[] = [mockCatcher]; + + describe("component visibility", () => { + it("does not render when no runners on base", () => { + const wrapper = mount(RunnersOnBase, { + global: { plugins: [pinia] }, + props: { + runners: { first: null, second: null, third: null }, + fieldingLineup: mockFieldingLineup, + battingTeamColor: "#3b82f6", + fieldingTeamColor: "#10b981", + battingTeamAbbrev: "BOS", + fieldingTeamAbbrev: "NYY", + }, + }); + + expect(wrapper.find(".runners-on-base-container").exists()).toBe( + false, + ); + }); + + it("renders when at least one runner on base", () => { + const wrapper = mount(RunnersOnBase, { + global: { plugins: [pinia] }, + props: { + runners: { + first: null, + second: mockRunnerSecond, + third: null, + }, + fieldingLineup: mockFieldingLineup, + battingTeamColor: "#3b82f6", + fieldingTeamColor: "#10b981", + battingTeamAbbrev: "BOS", + fieldingTeamAbbrev: "NYY", + }, + }); + + expect(wrapper.find(".runners-on-base-container").exists()).toBe( + true, + ); + }); + + it("renders when bases loaded", () => { + const wrapper = mount(RunnersOnBase, { + global: { plugins: [pinia] }, + props: { + runners: { + first: mockRunnerFirst, + second: mockRunnerSecond, + third: mockRunnerThird, + }, + fieldingLineup: mockFieldingLineup, + battingTeamColor: "#3b82f6", + fieldingTeamColor: "#10b981", + battingTeamAbbrev: "BOS", + fieldingTeamAbbrev: "NYY", + }, + }); + + expect(wrapper.find(".runners-on-base-container").exists()).toBe( + true, + ); + }); + }); + + describe("runner cards", () => { + it("renders three RunnerCard components", () => { + const wrapper = mount(RunnersOnBase, { + global: { plugins: [pinia] }, + props: { + runners: { + first: mockRunnerFirst, + second: null, + third: null, + }, + fieldingLineup: mockFieldingLineup, + battingTeamColor: "#3b82f6", + fieldingTeamColor: "#10b981", + battingTeamAbbrev: "BOS", + fieldingTeamAbbrev: "NYY", + }, + }); + + const runnerCards = wrapper.findAllComponents(RunnerCard); + expect(runnerCards).toHaveLength(3); + }); + + it("passes correct base labels to RunnerCard components", async () => { + const wrapper = mount(RunnersOnBase, { + global: { plugins: [pinia] }, + props: { + runners: { + first: mockRunnerFirst, + second: null, + third: null, + }, + fieldingLineup: mockFieldingLineup, + battingTeamColor: "#3b82f6", + fieldingTeamColor: "#10b981", + battingTeamAbbrev: "BOS", + fieldingTeamAbbrev: "NYY", + }, + }); + + await wrapper.vm.$nextTick(); + + const runnerCards = wrapper.findAllComponents(RunnerCard); + expect(runnerCards.length).toBeGreaterThanOrEqual(3); + expect(runnerCards[0].props("base")).toBe("1B"); + expect(runnerCards[1].props("base")).toBe("2B"); + expect(runnerCards[2].props("base")).toBe("3B"); + }); + + it("passes runner data to RunnerCard components", () => { + const wrapper = mount(RunnersOnBase, { + global: { plugins: [pinia] }, + props: { + runners: { + first: mockRunnerFirst, + second: mockRunnerSecond, + third: null, + }, + fieldingLineup: mockFieldingLineup, + battingTeamColor: "#3b82f6", + fieldingTeamColor: "#10b981", + battingTeamAbbrev: "BOS", + fieldingTeamAbbrev: "NYY", + }, + }); + + const runnerCards = wrapper.findAllComponents(RunnerCard); + expect(runnerCards[0].props("runner")).toEqual(mockRunnerFirst); + expect(runnerCards[1].props("runner")).toEqual(mockRunnerSecond); + expect(runnerCards[2].props("runner")).toBeNull(); + }); + + it("passes team color to RunnerCard components", () => { + const wrapper = mount(RunnersOnBase, { + global: { plugins: [pinia] }, + props: { + runners: { + first: mockRunnerFirst, + second: null, + third: null, + }, + fieldingLineup: mockFieldingLineup, + battingTeamColor: "#ff0000", + fieldingTeamColor: "#00ff00", + battingTeamAbbrev: "BOS", + fieldingTeamAbbrev: "NYY", + }, + }); + + const runnerCards = wrapper.findAllComponents(RunnerCard); + runnerCards.forEach((card) => { + expect(card.props("teamColor")).toBe("#ff0000"); + }); + }); + }); + + describe("catcher display", () => { + it("shows collapsed catcher card by default", () => { + const wrapper = mount(RunnersOnBase, { + global: { plugins: [pinia] }, + props: { + runners: { + first: mockRunnerFirst, + second: null, + third: null, + }, + fieldingLineup: mockFieldingLineup, + battingTeamColor: "#3b82f6", + fieldingTeamColor: "#10b981", + battingTeamAbbrev: "BOS", + fieldingTeamAbbrev: "NYY", + }, + }); + + // Collapsed state shows border-l-4, expanded state shows .matchup-card + expect(wrapper.find(".border-l-4.border-gray-600").exists()).toBe( + true, + ); + expect(wrapper.find(".matchup-card").exists()).toBe(false); + }); + + it("displays catcher name", () => { + const wrapper = mount(RunnersOnBase, { + global: { plugins: [pinia] }, + props: { + runners: { + first: mockRunnerFirst, + second: null, + third: null, + }, + fieldingLineup: mockFieldingLineup, + battingTeamColor: "#3b82f6", + fieldingTeamColor: "#10b981", + battingTeamAbbrev: "BOS", + fieldingTeamAbbrev: "NYY", + }, + }); + + expect(wrapper.text()).toContain("Buster Posey"); + }); + + it('shows "Unknown Catcher" when no catcher in lineup', () => { + const wrapper = mount(RunnersOnBase, { + global: { plugins: [pinia] }, + props: { + runners: { + first: mockRunnerFirst, + second: null, + third: null, + }, + fieldingLineup: [], + battingTeamColor: "#3b82f6", + fieldingTeamColor: "#10b981", + battingTeamAbbrev: "BOS", + fieldingTeamAbbrev: "NYY", + }, + }); + + expect(wrapper.text()).toContain("Unknown Catcher"); + }); + }); + + describe("runner selection", () => { + it("expands catcher card when runner is selected", async () => { + const gameStore = useGameStore(); + gameStore.setGameState({ + id: 1, + home_team_id: 1, + away_team_id: 2, + status: "active", + inning: 1, + half: "top", + outs: 0, + home_score: 0, + away_score: 0, + home_team_abbrev: "NYY", + away_team_abbrev: "BOS", + home_team_dice_color: "3b82f6", + current_batter: null, + current_pitcher: null, + on_first: null, + on_second: null, + on_third: null, + decision_phase: "idle", + play_count: 0, + }); + gameStore.updateLineup(1, [ + { + id: 1, + lineup_id: 1, + team_id: 1, + batting_order: 1, + position: "LF", + is_active: true, + player: { + id: 101, + name: "Mike Trout", + image: "https://example.com/trout.jpg", + }, + }, + ]); + + const wrapper = mount(RunnersOnBase, { + global: { plugins: [pinia] }, + props: { + runners: { + first: mockRunnerFirst, + second: null, + third: null, + }, + fieldingLineup: mockFieldingLineup, + battingTeamColor: "#3b82f6", + fieldingTeamColor: "#10b981", + battingTeamAbbrev: "BOS", + fieldingTeamAbbrev: "NYY", + }, + }); + + const runnerCards = wrapper.findAllComponents(RunnerCard); + await runnerCards[0].trigger("click"); + + // When runner selected, collapsed state hidden and expanded state shown + expect(wrapper.find(".border-l-4.border-gray-600").exists()).toBe( + false, + ); + expect(wrapper.find(".matchup-card").exists()).toBe(true); + }); + + it("collapses catcher card when runner is deselected", async () => { + const gameStore = useGameStore(); + gameStore.setGameState({ + id: 1, + home_team_id: 1, + away_team_id: 2, + status: "active", + inning: 1, + half: "top", + outs: 0, + home_score: 0, + away_score: 0, + home_team_abbrev: "NYY", + away_team_abbrev: "BOS", + home_team_dice_color: "3b82f6", + current_batter: null, + current_pitcher: null, + on_first: null, + on_second: null, + on_third: null, + decision_phase: "idle", + play_count: 0, + }); + gameStore.updateLineup(1, [ + { + id: 1, + lineup_id: 1, + team_id: 1, + batting_order: 1, + position: "LF", + is_active: true, + player: { + id: 101, + name: "Mike Trout", + image: "https://example.com/trout.jpg", + }, + }, + ]); + + const wrapper = mount(RunnersOnBase, { + global: { plugins: [pinia] }, + props: { + runners: { + first: mockRunnerFirst, + second: null, + third: null, + }, + fieldingLineup: mockFieldingLineup, + battingTeamColor: "#3b82f6", + fieldingTeamColor: "#10b981", + battingTeamAbbrev: "BOS", + fieldingTeamAbbrev: "NYY", + }, + }); + + const runnerCards = wrapper.findAllComponents(RunnerCard); + + // Click to expand + await runnerCards[0].trigger("click"); + expect(wrapper.find(".matchup-card").exists()).toBe(true); + + // Click again to collapse + await runnerCards[0].trigger("click"); + expect(wrapper.find(".border-l-4.border-gray-600").exists()).toBe( + true, + ); + expect(wrapper.find(".matchup-card").exists()).toBe(false); + }); + + it("switches selection when clicking different runner", async () => { + const gameStore = useGameStore(); + gameStore.setGameState({ + id: 1, + home_team_id: 1, + away_team_id: 2, + status: "active", + inning: 1, + half: "top", + outs: 0, + home_score: 0, + away_score: 0, + home_team_abbrev: "NYY", + away_team_abbrev: "BOS", + home_team_dice_color: "3b82f6", + current_batter: null, + current_pitcher: null, + on_first: null, + on_second: null, + on_third: null, + decision_phase: "idle", + play_count: 0, + }); + gameStore.updateLineup(1, [ + { + id: 1, + lineup_id: 1, + team_id: 1, + batting_order: 1, + position: "LF", + is_active: true, + player: { + id: 101, + name: "Mike Trout", + image: "https://example.com/trout.jpg", + }, + }, + { + id: 2, + lineup_id: 2, + team_id: 1, + batting_order: 2, + position: "CF", + is_active: true, + player: { + id: 102, + name: "Aaron Judge", + image: "https://example.com/judge.jpg", + }, + }, + ]); + + const wrapper = mount(RunnersOnBase, { + global: { plugins: [pinia] }, + props: { + runners: { + first: mockRunnerFirst, + second: mockRunnerSecond, + third: null, + }, + fieldingLineup: mockFieldingLineup, + battingTeamColor: "#3b82f6", + fieldingTeamColor: "#10b981", + battingTeamAbbrev: "BOS", + fieldingTeamAbbrev: "NYY", + }, + }); + + const runnerCards = wrapper.findAllComponents(RunnerCard); + + // Select first runner + await runnerCards[0].trigger("click"); + expect(runnerCards[0].props("isExpanded")).toBe(true); + expect(runnerCards[1].props("isExpanded")).toBe(false); + + // Select second runner + await runnerCards[1].trigger("click"); + expect(runnerCards[0].props("isExpanded")).toBe(false); + expect(runnerCards[1].props("isExpanded")).toBe(true); + }); + }); + + describe("team information", () => { + it("displays team abbreviations when provided", async () => { + const gameStore = useGameStore(); + gameStore.setGameState({ + id: 1, + home_team_id: 1, + away_team_id: 2, + status: "active", + inning: 1, + half: "top", + outs: 0, + home_score: 0, + away_score: 0, + home_team_abbrev: "NYY", + away_team_abbrev: "BOS", + home_team_dice_color: "3b82f6", + current_batter: null, + current_pitcher: null, + on_first: null, + on_second: null, + on_third: null, + decision_phase: "idle", + play_count: 0, + }); + gameStore.updateLineup(1, [ + { + id: 1, + lineup_id: 1, + team_id: 1, + batting_order: 1, + position: "LF", + is_active: true, + player: { + id: 101, + name: "Mike Trout", + image: "https://example.com/trout.jpg", + }, + }, + ]); + + const wrapper = mount(RunnersOnBase, { + global: { plugins: [pinia] }, + props: { + runners: { + first: mockRunnerFirst, + second: null, + third: null, + }, + fieldingLineup: mockFieldingLineup, + battingTeamColor: "#3b82f6", + fieldingTeamColor: "#10b981", + battingTeamAbbrev: "BOS", + fieldingTeamAbbrev: "NYY", + }, + }); + + // Click runner to expand and show team abbreviation + const runnerCards = wrapper.findAllComponents(RunnerCard); + await runnerCards[0].trigger("click"); + + expect(wrapper.text()).toContain("NYY"); + }); + + it("uses default colors when not provided", () => { + const wrapper = mount(RunnersOnBase, { + global: { plugins: [pinia] }, + props: { + runners: { + first: mockRunnerFirst, + second: null, + third: null, + }, + fieldingLineup: mockFieldingLineup, + }, + }); + + const runnerCards = wrapper.findAllComponents(RunnerCard); + expect(runnerCards[0].props("teamColor")).toBe("#3b82f6"); + }); + }); +});