import { fireEvent } from "@testing-library/react" import { beforeEach, describe, expect, it, vi } from "vitest" import { mockUseTranslation } from "@/test/mocks/libraries/i18n" import { renderWithProviders } from "@/test/test-utils" import { setupAudioTestEnvironment } from "@/test/utils/tauri-audio-test-utils" import { ChannelStrip } from "../../mixer/channel-strip" // Mock the level meter component BEFORE importing ChannelStrip vi.mock("@/features/fairlight-audio/components/meters/level-meter", () => ({ LevelMeter: ({ audioContext, source }: { audioContext?: AudioContext; source?: AnalyserNode }) => (
{audioContext && source ? "Meter Active" : "Meter Inactive"}
), })) // Mock the surround panner component vi.mock("../../mixer/surround-panner", () => ({ SurroundPanner: ({ pan, onPanChange }: { pan: number; onPanChange: (value: number) => void }) => (
onPanChange(Number(e.target.value))} />
), })) describe("ChannelStrip", () => { let testEnv: ReturnType const mockProps = { channelId: "channel-1", name: "Channel 1", type: "stereo" as const, volume: 75, pan: 0, muted: false, solo: false, armed: false, onVolumeChange: vi.fn(), onPanChange: vi.fn(), onMute: vi.fn(), onSolo: vi.fn(), onArm: vi.fn(), onSettings: vi.fn(), } beforeEach(() => { testEnv = setupAudioTestEnvironment() vi.clearAllMocks() // Mock translations mockUseTranslation.mockReturnValue({ t: (key: string) => { const translations: Record = { "fairlightAudio.mixer.channelStrip.eq": "EQ", "fairlightAudio.mixer.channelStrip.send1": "Send 1", "fairlightAudio.mixer.channelStrip.send2": "Send 2", "fairlightAudio.mixer.channelStrip.pan": "PAN", "fairlightAudio.mixer.channelStrip.right": "R", "fairlightAudio.mixer.channelStrip.outputs.main": "Main", "fairlightAudio.mixer.channelStrip.outputs.bus1": "Bus 1", "fairlightAudio.mixer.channelStrip.outputs.bus2": "Bus 2", } return translations[key] || key }, i18n: { language: "en" } as any, ready: true, }) }) afterEach(() => { testEnv?.cleanup() }) describe("rendering", () => { it("renders channel name", () => { const { getByText } = renderWithProviders() expect(getByText("Channel 1")).toBeInTheDocument() }) it("renders channel type indicator", () => { const { getByText } = renderWithProviders() expect(getByText("STEREO")).toBeInTheDocument() }) it("renders mono type correctly", () => { const { getByText } = renderWithProviders() expect(getByText("MONO")).toBeInTheDocument() }) it("renders surround type correctly", () => { const { getByText } = renderWithProviders() expect(getByText("SURROUND")).toBeInTheDocument() }) it("renders EQ button", () => { const { getByText } = renderWithProviders() expect(getByText("EQ")).toBeInTheDocument() }) it("renders effects sends", () => { const { getByText } = renderWithProviders() expect(getByText("Send 1")).toBeInTheDocument() expect(getByText("Send 2")).toBeInTheDocument() }) it("renders pan control", () => { const { getByText } = renderWithProviders() expect(getByText("PAN")).toBeInTheDocument() }) it("renders arm button", () => { const { getByText } = renderWithProviders() expect(getByText("R")).toBeInTheDocument() }) it("renders output routing selector", () => { const { getByText } = renderWithProviders() expect(getByText("Main")).toBeInTheDocument() }) it("applies custom className", () => { const { container } = renderWithProviders() const channelStrip = container.querySelector(".custom-class") expect(channelStrip).toBeInTheDocument() }) }) describe("interactions", () => { it("calls onSettings when settings button is clicked", () => { const { container } = renderWithProviders() const settingsButton = container.querySelector('[class*="hover:bg-zinc-800"]')! fireEvent.click(settingsButton) expect(mockProps.onSettings).toHaveBeenCalled() }) it("calls onPanChange when pan slider is changed", () => { const { container } = renderWithProviders() const panSlider = container.querySelector('input[type="range"]')! fireEvent.change(panSlider, { target: { value: "50" } }) expect(mockProps.onPanChange).toHaveBeenCalledWith(50) }) it("calls onArm when arm button is clicked", () => { const { getByText } = renderWithProviders() const armButton = getByText("R") fireEvent.click(armButton) expect(mockProps.onArm).toHaveBeenCalled() }) it("toggles EQ section when EQ button is clicked", () => { const { getByText } = renderWithProviders() const eqButton = getByText("EQ") // Initially showEq is false fireEvent.click(eqButton) // After click, showEq should be true (though we can't directly test state) expect(eqButton).toBeInTheDocument() }) }) describe("armed state", () => { it("applies armed styles when armed is true", () => { const { getByText } = renderWithProviders() const armButton = getByText("R") expect(armButton).toHaveClass("bg-red-600") expect(armButton).toHaveClass("text-white") }) it("applies default styles when armed is false", () => { const { getByText } = renderWithProviders() const armButton = getByText("R") expect(armButton).toHaveClass("bg-zinc-800") expect(armButton).toHaveClass("text-zinc-500") }) }) describe("level meter integration", () => { it("renders level meter when audioContext and analyser are provided", () => { const mockAudioContext = new testEnv.webAudio.AudioContext() as unknown as AudioContext const mockAnalyser = mockAudioContext.createAnalyser() as unknown as AnalyserNode const { getByTestId } = renderWithProviders( , ) const levelMeter = getByTestId("level-meter") expect(levelMeter).toBeInTheDocument() expect(levelMeter).toHaveTextContent("Meter Active") }) it("does not render level meter when audioContext is missing", () => { const { queryByTestId } = renderWithProviders() const levelMeter = queryByTestId("level-meter") expect(levelMeter).toBeNull() }) }) describe("pan control", () => { it("displays pan knob at correct position", () => { const { container } = renderWithProviders() const panKnob = container.querySelector(".absolute.top-1\\/2") expect(panKnob).toHaveStyle({ left: "75%" }) }) it("displays pan knob at center when pan is 0", () => { const { container } = renderWithProviders() const panKnob = container.querySelector(".absolute.top-1\\/2") expect(panKnob).toHaveStyle({ left: "50%" }) }) it("displays pan knob at left when pan is -100", () => { const { container } = renderWithProviders() const panKnob = container.querySelector(".absolute.top-1\\/2") expect(panKnob).toHaveStyle({ left: "0%" }) }) it("displays pan knob at right when pan is 100", () => { const { container } = renderWithProviders() const panKnob = container.querySelector(".absolute.top-1\\/2") expect(panKnob).toHaveStyle({ left: "100%" }) }) }) describe("fader integration", () => { it("passes correct props to Fader component", () => { const { container } = renderWithProviders() // Find the fader by looking for its container structure const faderContainer = container.querySelector(".flex-1.flex.justify-center.gap-1") expect(faderContainer).toBeInTheDocument() // Fader should be rendered with dbScale // (More detailed testing would be in Fader component tests) }) it("propagates volume changes from Fader", () => { // This test would be more complete with Fader component rendered // For now, we know the prop is passed correctly const { container } = renderWithProviders() expect(container).toBeInTheDocument() }) }) describe("channel type styling", () => { it("applies stereo styling", () => { const { getByText } = renderWithProviders() const typeIndicator = getByText("STEREO") expect(typeIndicator).toHaveClass("bg-blue-900") expect(typeIndicator).toHaveClass("text-blue-300") }) it("applies mono styling", () => { const { getByText } = renderWithProviders() const typeIndicator = getByText("MONO") expect(typeIndicator).toHaveClass("bg-zinc-800") expect(typeIndicator).toHaveClass("text-zinc-400") }) it("applies surround styling", () => { const { getByText } = renderWithProviders() const typeIndicator = getByText("SURROUND") expect(typeIndicator).toHaveClass("bg-purple-900") expect(typeIndicator).toHaveClass("text-purple-300") }) }) })