import { describe, it, expect } from 'vitest'; import { parseProgressLine, isProgressLine } from '../services/progress-parser'; describe('parseProgressLine', () => { describe('standard progress lines', () => { it('parses a normal progress line', () => { const result = parseProgressLine( '[download] 45.2% of ~150.00MiB at 1.23MiB/s ETA 00:42' ); expect(result).toEqual({ percent: 45.2, speed: '1.23MiB/s', eta: '00:42', totalSize: '~150.00MiB', }); }); it('parses 100% completion', () => { const result = parseProgressLine( '[download] 100% of 150.00MiB at 2.50MiB/s ETA 00:00' ); expect(result).toEqual({ percent: 100, speed: '2.50MiB/s', eta: '00:00', totalSize: '150.00MiB', }); }); it('parses a line with 0% progress', () => { const result = parseProgressLine( '[download] 0.0% of ~500.00MiB at 0.50MiB/s ETA 16:40' ); expect(result).toEqual({ percent: 0, speed: '0.50MiB/s', eta: '16:40', totalSize: '~500.00MiB', }); }); it('parses a line with GiB total size', () => { const result = parseProgressLine( '[download] 12.3% of ~2.50GiB at 10.00MiB/s ETA 03:45' ); expect(result).toEqual({ percent: 12.3, speed: '10.00MiB/s', eta: '03:45', totalSize: '~2.50GiB', }); }); it('parses a line with KiB speed', () => { const result = parseProgressLine( '[download] 5.0% of ~80.00MiB at 512.00KiB/s ETA 02:37' ); expect(result).toEqual({ percent: 5.0, speed: '512.00KiB/s', eta: '02:37', totalSize: '~80.00MiB', }); }); }); describe('unknown values', () => { it('handles Unknown speed', () => { const result = parseProgressLine( '[download] 10.0% of ~150.00MiB at Unknown speed ETA Unknown' ); expect(result).toEqual({ percent: 10.0, speed: '', eta: '', totalSize: '~150.00MiB', }); }); it('handles Unknown ETA with known speed', () => { const result = parseProgressLine( '[download] 25.0% of ~300.00MiB at 5.00MiB/s ETA Unknown' ); expect(result).toEqual({ percent: 25.0, speed: '5.00MiB/s', eta: '', totalSize: '~300.00MiB', }); }); }); describe('non-progress lines (returns null)', () => { it('returns null for empty string', () => { expect(parseProgressLine('')).toBeNull(); }); it('returns null for whitespace', () => { expect(parseProgressLine(' ')).toBeNull(); }); it('returns null for info lines', () => { expect( parseProgressLine('[info] Writing video metadata as JSON to: file.json') ).toBeNull(); }); it('returns null for merge lines', () => { expect( parseProgressLine('[Merger] Merging formats into "video.mp4"') ).toBeNull(); }); it('returns null for postprocessor lines', () => { expect( parseProgressLine('[EmbedSubtitle] Embedding subtitles in "video.mp4"') ).toBeNull(); }); it('returns null for destination lines', () => { expect( parseProgressLine('[download] Destination: /path/to/file.mp4') ).toBeNull(); }); it('returns null for "has already been downloaded" lines', () => { expect( parseProgressLine('[download] Video abc123 has already been downloaded') ).toBeNull(); }); it('returns null for plain text output', () => { expect(parseProgressLine('/path/to/downloaded/file.mp4')).toBeNull(); }); it('returns null for Deleting lines', () => { expect( parseProgressLine('Deleting original file /tmp/video.webm') ).toBeNull(); }); }); describe('edge cases', () => { it('handles lines with leading whitespace', () => { const result = parseProgressLine( ' [download] 50.0% of ~200.00MiB at 3.00MiB/s ETA 00:33 ' ); expect(result).toEqual({ percent: 50.0, speed: '3.00MiB/s', eta: '00:33', totalSize: '~200.00MiB', }); }); it('clamps percent to 100 maximum', () => { // Shouldn't happen in practice, but defensive const result = parseProgressLine( '[download] 105.0% of ~100.00MiB at 1.00MiB/s ETA 00:00' ); expect(result).not.toBeNull(); expect(result!.percent).toBe(100); }); it('handles no tilde prefix on total size', () => { const result = parseProgressLine( '[download] 75.0% of 120.00MiB at 4.00MiB/s ETA 00:10' ); expect(result).toEqual({ percent: 75.0, speed: '4.00MiB/s', eta: '00:10', totalSize: '120.00MiB', }); }); }); describe('multi-stream download lines', () => { it('parses the first stream progress normally', () => { // When downloading video+audio separately, yt-dlp shows progress for each const result = parseProgressLine( '[download] 30.0% of ~80.00MiB at 2.00MiB/s ETA 00:28' ); expect(result).not.toBeNull(); expect(result!.percent).toBe(30.0); }); it('parses the second stream progress (resets to 0%)', () => { // The second stream starts at 0% again const result = parseProgressLine( '[download] 5.0% of ~20.00MiB at 1.50MiB/s ETA 00:13' ); expect(result).not.toBeNull(); expect(result!.percent).toBe(5.0); }); }); }); describe('isProgressLine', () => { it('returns true for a progress line', () => { expect( isProgressLine('[download] 45.2% of ~150.00MiB at 1.23MiB/s ETA 00:42') ).toBe(true); }); it('returns true for 100% line', () => { expect( isProgressLine('[download] 100% of 150.00MiB at 2.50MiB/s ETA 00:00') ).toBe(true); }); it('returns false for destination line (no %)', () => { expect( isProgressLine('[download] Destination: /path/to/file.mp4') ).toBe(false); }); it('returns false for info line', () => { expect(isProgressLine('[info] Available formats for abc123')).toBe(false); }); it('returns false for empty line', () => { expect(isProgressLine('')).toBe(false); }); it('returns false for filepath output', () => { expect(isProgressLine('/media/youtube/channel/video.mp4')).toBe(false); }); });