189 lines
4.8 KiB
TypeScript
189 lines
4.8 KiB
TypeScript
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: [],
|
|
},
|
|
}));
|
|
|
|
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', () => {
|
|
let mockVideo: HTMLVideoElement;
|
|
|
|
beforeEach(() => {
|
|
mockVideo = createMockVideoElement();
|
|
setVideoElement(mockVideo);
|
|
Object.defineProperty(document, 'fullscreenElement', {
|
|
value: null,
|
|
writable: true,
|
|
configurable: true,
|
|
});
|
|
});
|
|
|
|
it('isFullscreenActive returns false when not fullscreen', () => {
|
|
expect(isFullscreenActive()).toBe(false);
|
|
});
|
|
|
|
it('toggleFullscreen calls requestFullscreen on document.documentElement', () => {
|
|
const spy = vi.fn().mockResolvedValue(undefined);
|
|
document.documentElement.requestFullscreen = spy;
|
|
toggleFullscreen();
|
|
expect(spy).toHaveBeenCalled();
|
|
});
|
|
|
|
it('isFullscreenActive returns true when fullscreen element exists', () => {
|
|
Object.defineProperty(document, 'fullscreenElement', {
|
|
value: mockVideo,
|
|
writable: true,
|
|
configurable: true,
|
|
});
|
|
expect(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);
|
|
});
|
|
});
|