fix: QA round 2 — playhead guard, controls position, caption lift, Tauri fullscreen, timeline sync

- Guard drawPlayhead on session.duration > 0 in PlayerTimeline
- Move floating controls pill from bottom: 24px to 48px
- Add playerControlsVisible prop to VideoPlayer; captions slide up 160px when controls visible
- Replace web Fullscreen API with Tauri getCurrentWindow().setFullscreen() (WKWebView compat)
- Add core:window:allow-set-fullscreen and core:window:allow-is-fullscreen permissions
- Tie PlayerTimeline visibility to showPlayerControls; remove unused proximity logic

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-09-22 20:16:10 -04:00
parent 0f2a523cc2
commit 2e36d16bfe
10 changed files with 225 additions and 58 deletions

View File

@@ -11,6 +11,15 @@ vi.mock('$lib/stores/videoSession.svelte', () => ({
},
}));
// 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,
@@ -70,36 +79,30 @@ describe('playback — PiP helpers', () => {
});
describe('playback — fullscreen helpers', () => {
let mockVideo: HTMLVideoElement;
beforeEach(() => {
mockVideo = createMockVideoElement();
setVideoElement(mockVideo);
Object.defineProperty(document, 'fullscreenElement', {
value: null,
writable: true,
configurable: true,
});
mockTauriWindow.isFullscreen.mockResolvedValue(false);
mockTauriWindow.setFullscreen.mockResolvedValue(undefined);
});
it('isFullscreenActive returns false when not fullscreen', () => {
expect(isFullscreenActive()).toBe(false);
it('isFullscreenActive returns false when not fullscreen', async () => {
expect(await 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('toggleFullscreen calls setFullscreen(true) when not fullscreen', async () => {
mockTauriWindow.isFullscreen.mockResolvedValue(false);
await toggleFullscreen();
expect(mockTauriWindow.setFullscreen).toHaveBeenCalledWith(true);
});
it('isFullscreenActive returns true when fullscreen element exists', () => {
Object.defineProperty(document, 'fullscreenElement', {
value: mockVideo,
writable: true,
configurable: true,
});
expect(isFullscreenActive()).toBe(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);
});
});