import { describe, it, expect } from 'vitest' import { summarizeQueue, sameVideo } from '../src/renderer/src/lib/queueStats' import type { DownloadItem } from '../src/renderer/src/store/downloads' // Minimal item factory — only the fields summarizeQueue reads. function item(overrides: Partial): DownloadItem { return { id: Math.random().toString(36).slice(2), url: 'https://youtube.com/watch?v=x', title: 'x', kind: 'video', quality: '1080p', status: 'queued', progress: 0, ...overrides } } describe('summarizeQueue', () => { it('counts statuses and flags active', () => { const s = summarizeQueue([ item({ status: 'downloading', progress: 0.5 }), item({ status: 'queued' }), item({ status: 'error' }), item({ status: 'completed', progress: 1 }), item({ status: 'canceled' }) ]) expect(s.downloading).toBe(1) expect(s.queued).toBe(1) expect(s.failed).toBe(1) expect(s.active).toBe(true) }) it('is inactive and zeroed when nothing is downloading or queued', () => { const s = summarizeQueue([item({ status: 'completed', progress: 1 })]) expect(s.active).toBe(false) expect(s.progress).toBe(0) expect(s.speedLabel).toBe('') expect(s.etaLabel).toBe('') }) it('averages progress over downloading + queued (queued counts as 0%)', () => { // one at 80%, one queued at 0% → 40% const s = summarizeQueue([ item({ status: 'downloading', progress: 0.8 }), item({ status: 'queued' }) ]) expect(s.progress).toBeCloseTo(0.4, 5) }) it('sums raw download rates into one formatted label', () => { const MB = 1024 * 1024 const s = summarizeQueue([ item({ status: 'downloading', progress: 0.1, speedBytesPerSec: 4 * MB }), item({ status: 'downloading', progress: 0.1, speedBytesPerSec: 2 * MB }) ]) // 4 MB/s + 2 MB/s = 6 MB/s expect(s.speedLabel).toBe('6.0 MB/s') }) it('leaves the speed label empty when no item reports a rate', () => { const s = summarizeQueue([item({ status: 'downloading', progress: 0.1 })]) expect(s.speedLabel).toBe('') }) it('reports the longest active ETA as the time left', () => { const s = summarizeQueue([ item({ status: 'downloading', progress: 0.5, etaSeconds: 30 }), item({ status: 'downloading', progress: 0.2, etaSeconds: 130 }) ]) expect(s.etaLabel).toBe('2:10') }) }) describe('sameVideo', () => { it('matches identical URLs', () => { expect(sameVideo('https://x.com/a', 'https://x.com/a')).toBe(true) }) it('matches YouTube links by video id across watch/youtu.be/extra params', () => { expect( sameVideo('https://www.youtube.com/watch?v=dQw4w9WgXcQ', 'https://youtu.be/dQw4w9WgXcQ') ).toBe(true) expect( sameVideo( 'https://youtube.com/watch?v=dQw4w9WgXcQ&list=PL123', 'https://www.youtube.com/watch?v=dQw4w9WgXcQ' ) ).toBe(true) }) it('does not match different videos', () => { expect( sameVideo( 'https://youtube.com/watch?v=aaaaaaaaaaa', 'https://youtube.com/watch?v=bbbbbbbbbbb' ) ).toBe(false) }) it('normalises www. and trailing slash for non-YouTube URLs', () => { expect(sameVideo('https://www.vimeo.com/123/', 'https://vimeo.com/123')).toBe(true) }) })