import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; import { render, screen, fireEvent, act } from '@testing-library/react'; import { GenerationCard } from './generation-card'; import type { GenerationResponse } from '@/lib/api'; // Mock dependencies vi.mock('@/hooks/use-websocket', () => ({ useGenerationWebSocket: () => ({ lastMessage: null }) })); interface SliderMockProps { value: number[]; onValueChange: (v: number[]) => void; className?: string; } vi.mock('@/components/ui/slider', () => ({ Slider: ({ value, onValueChange, className, ...props }: SliderMockProps) => ( onValueChange([parseFloat(e.target.value)])} className={className} {...props} /> ) })); // Mock Audio element const mockPlay = vi.fn().mockResolvedValue(undefined); const mockPause = vi.fn(); describe('User Story Verification: Playback Control', () => { const originalPlay = window.HTMLMediaElement.prototype.play; const originalPause = window.HTMLMediaElement.prototype.pause; beforeEach(() => { window.HTMLMediaElement.prototype.play = mockPlay; window.HTMLMediaElement.prototype.pause = mockPause; }); afterEach(() => { window.HTMLMediaElement.prototype.play = originalPlay; window.HTMLMediaElement.prototype.pause = originalPause; vi.clearAllMocks(); }); const mockGeneration: GenerationResponse = { id: 'test-gen-123', status: 'completed', prompt: 'A test track', audio_path: '/music/test.wav', created_at: new Date().toISOString(), metadata: { prompt: 'A test track', analysis: { style: 'lo-fi', tempo: 90, mood: 'chill' } } }; it('Scenario: User initiates playback and updates UI', async () => { render(); // 1. Validate Initial State // "play" button should be visible const playButton = screen.getByLabelText('Play'); expect(playButton).toBeInTheDocument(); // "pause" button should NOT be visible expect(screen.queryByLabelText('Pause')).not.toBeInTheDocument(); // Card should NOT have the highlight class (checking specifically for the ring-primary class) const card = screen.getByText('A test track').closest('.bg-card'); expect(card).not.toHaveClass('ring-primary'); // 2. Action: Press "play" button await act(async () => { fireEvent.click(playButton); }); // 3. Verify Functionality: Initiate playback expect(mockPlay).toHaveBeenCalledTimes(1); // 4. Validate UI Rendering: Update to reflect playing state // "pause" button SHOULD be visible/enabled const pauseButton = screen.getByLabelText('Pause'); expect(pauseButton).toBeInTheDocument(); // "play" button should NOT be visible expect(screen.queryByLabelText('Play')).not.toBeInTheDocument(); // 5. Validate UI Rendering: Visual Highlight // Card SHOULD have the highlight class expect(card).toHaveClass('ring-primary'); expect(card).toHaveClass('border-primary'); expect(card).toHaveClass('shadow-[0_0_15px_rgba(var(--primary),0.2)]'); // 6. Action: Press "pause" button await act(async () => { fireEvent.click(pauseButton); }); // 7. Verify Functionality: Pause playback expect(mockPause).toHaveBeenCalledTimes(1); // 8. Validate UI Rendering: Return to paused state expect(screen.getByLabelText('Play')).toBeInTheDocument(); expect(card).not.toHaveClass('ring-primary'); }); });