import { describe, it, expect, vi, beforeEach } from 'vitest'; // Mock the videoSession store (playback.ts imports it) vi.mock('$lib/stores/videoSession.svelte', () => ({ session: { duration: 120, currentTime: 0, fps: 30, isPlaying: false, keyframePositions: [], }, })); // Mock Tauri window API for fullscreen const mockTauriWindow = { isFullscreen: vi.fn().mockResolvedValue(false), setFullscreen: vi.fn().mockResolvedValue(undefined), }; vi.mock('@tauri-apps/api/window', () => ({ getCurrentWindow: () => mockTauriWindow, })); import { setVideoElement, togglePiP, isPiPActive, toggleFullscreen, isFullscreenActive, setPlaybackRate, getPlaybackRate, adjustShuttle, } from '$lib/transport/playback'; function createMockVideoElement(): HTMLVideoElement { const el = document.createElement('video'); // PiP (el as any).requestPictureInPicture = vi.fn().mockResolvedValue({}); // Fullscreen el.requestFullscreen = vi.fn().mockResolvedValue(undefined); return el; } describe('playback — PiP helpers', () => { let mockVideo: HTMLVideoElement; beforeEach(() => { mockVideo = createMockVideoElement(); setVideoElement(mockVideo); // Reset PiP state Object.defineProperty(document, 'pictureInPictureElement', { value: null, writable: true, configurable: true, }); Object.defineProperty(document, 'pictureInPictureEnabled', { value: true, writable: true, configurable: true, }); }); it('isPiPActive returns false when no PiP element', () => { expect(isPiPActive()).toBe(false); }); it('togglePiP calls requestPictureInPicture', () => { togglePiP(); expect((mockVideo as any).requestPictureInPicture).toHaveBeenCalled(); }); it('isPiPActive returns true when PiP element exists', () => { Object.defineProperty(document, 'pictureInPictureElement', { value: mockVideo, writable: true, configurable: true, }); expect(isPiPActive()).toBe(true); }); }); describe('playback — fullscreen helpers', () => { beforeEach(() => { mockTauriWindow.isFullscreen.mockResolvedValue(false); mockTauriWindow.setFullscreen.mockResolvedValue(undefined); }); it('isFullscreenActive returns false when not fullscreen', async () => { expect(await isFullscreenActive()).toBe(false); }); it('toggleFullscreen calls setFullscreen(true) when not fullscreen', async () => { mockTauriWindow.isFullscreen.mockResolvedValue(false); await toggleFullscreen(); expect(mockTauriWindow.setFullscreen).toHaveBeenCalledWith(true); }); it('toggleFullscreen calls setFullscreen(false) when fullscreen', async () => { mockTauriWindow.isFullscreen.mockResolvedValue(true); await toggleFullscreen(); expect(mockTauriWindow.setFullscreen).toHaveBeenCalledWith(false); }); it('isFullscreenActive returns true when fullscreen', async () => { mockTauriWindow.isFullscreen.mockResolvedValue(true); expect(await isFullscreenActive()).toBe(true); }); }); describe('playback — playback rate helpers', () => { let mockVideo: HTMLVideoElement; beforeEach(() => { mockVideo = createMockVideoElement(); setVideoElement(mockVideo); }); it('getPlaybackRate returns 1 by default', () => { expect(getPlaybackRate()).toBe(1); }); it('setPlaybackRate changes the video playback rate', () => { setPlaybackRate(1.5); expect(mockVideo.playbackRate).toBe(1.5); }); it('getPlaybackRate returns the current rate', () => { setPlaybackRate(2); expect(getPlaybackRate()).toBe(2); }); it('getPlaybackRate returns 1 when no video element', () => { setVideoElement(null); expect(getPlaybackRate()).toBe(1); }); }); describe('playback — adjustShuttle', () => { let mockVideo: HTMLVideoElement; beforeEach(() => { mockVideo = createMockVideoElement(); setVideoElement(mockVideo); mockVideo.playbackRate = 1; Object.defineProperty(mockVideo, 'paused', { value: true, writable: true, configurable: true, }); mockVideo.play = vi.fn().mockResolvedValue(undefined); }); it('increases rate by 0.25 when dir is 1', () => { const result = adjustShuttle(1); expect(result).toBe(1.25); expect(mockVideo.playbackRate).toBe(1.25); }); it('decreases rate by 0.25 when dir is -1', () => { const result = adjustShuttle(-1); expect(result).toBe(0.75); expect(mockVideo.playbackRate).toBe(0.75); }); it('reads current rate from video element, not external state', () => { mockVideo.playbackRate = 0.75; const result = adjustShuttle(1); expect(result).toBe(1); }); it('clamps to minimum 0.25', () => { mockVideo.playbackRate = 0.25; const result = adjustShuttle(-1); expect(result).toBe(0.25); }); it('clamps to maximum 4', () => { mockVideo.playbackRate = 4; const result = adjustShuttle(1); expect(result).toBe(4); }); it('starts playback if paused', () => { adjustShuttle(1); expect(mockVideo.play).toHaveBeenCalled(); }); it('returns 1 when no video element', () => { setVideoElement(null); expect(adjustShuttle(1)).toBe(1); }); });